Compare commits

...

16 Commits

Author SHA1 Message Date
Kantine Wrapper
f71bcf1ac7 chore: update build artifacts for v1.5.0 2026-02-26 10:39:49 +01:00
Kantine Wrapper
b041e9f318 chore: bump version to 1.5.0 and rollup changelog 2026-02-26 10:39:49 +01:00
Kantine Wrapper
984a897f73 chore: update build artifacts for v1.4.31 2026-02-26 10:35:28 +01:00
Kantine Wrapper
ae54d97d96 fix: target localStorage cleanup to kantine_ prefix to prevent host session loss (v1.4.31) 2026-02-26 10:35:28 +01:00
Kantine Wrapper
6ed0831f5d chore: update build artifacts for v1.4.30 2026-02-26 10:18:50 +01:00
Kantine Wrapper
ba75544f68 fix: session loss and order alarm rendering across unauthenticated sessions (v1.4.30) 2026-02-26 10:18:50 +01:00
Kantine Wrapper
7fdf7f6f3e chore: update build artifacts for v1.4.29 2026-02-26 10:01:39 +01:00
Kantine Wrapper
614f498d11 fix: defer favicon injection with setTimeout for htmlpreview compat (v1.4.29) 2026-02-26 10:01:17 +01:00
Kantine Wrapper
5f30696315 chore: update build artifacts for v1.4.28 2026-02-26 09:57:46 +01:00
Kantine Wrapper
cb5aa28f94 feat: custom favicon from user design, resized to 32x32 (v1.4.28) 2026-02-26 09:57:11 +01:00
Kantine Wrapper
bc1a91b7d7 chore: update build artifacts for v1.4.27 2026-02-26 09:50:46 +01:00
Kantine Wrapper
7d5beedfbb feat: build-time favicon injection from favicon.png via placeholder (v1.4.27) 2026-02-26 09:50:17 +01:00
Kantine Wrapper
0651d517b2 chore: update build artifacts for v1.4.26 2026-02-26 09:15:15 +01:00
Kantine Wrapper
c5e236e095 feat: favicon switched to PNG file served via raw GitHub URL (v1.4.26) 2026-02-26 09:15:10 +01:00
Kantine Wrapper
a5bff19796 chore: update build artifacts for v1.4.25 2026-02-26 09:03:12 +01:00
Kantine Wrapper
284f3d9a32 fix: dynamic favicon injection + push main to GitHub (v1.4.25) 2026-02-26 09:03:07 +01:00
10 changed files with 133 additions and 245 deletions

View File

@@ -7,7 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DIST_DIR="$SCRIPT_DIR/dist"
CSS_FILE="$SCRIPT_DIR/style.css"
JS_FILE="$SCRIPT_DIR/kantine.js"
FAVICON_FILE="$SCRIPT_DIR/favicon.svg"
FAVICON_FILE="$SCRIPT_DIR/favicon.png"
# === VERSION ===
if [ -f "$SCRIPT_DIR/version.txt" ]; then
@@ -26,14 +26,14 @@ if [ ! -f "$CSS_FILE" ]; then echo "ERROR: $CSS_FILE not found"; exit 1; fi
if [ ! -f "$JS_FILE" ]; then echo "ERROR: $JS_FILE not found"; exit 1; fi
if [ ! -f "$FAVICON_FILE" ]; then echo "ERROR: $FAVICON_FILE not found"; exit 1; fi
# Generate favicon Base64 data URI
# Generate favicon Base64 data URI from PNG
FAVICON_B64=$(base64 -w0 "$FAVICON_FILE")
FAVICON_URI="data:image/svg+xml;base64,${FAVICON_B64}"
FAVICON_URL="data:image/png;base64,${FAVICON_B64}"
CSS_CONTENT=$(cat "$CSS_FILE")
# Inject version into JS
JS_CONTENT=$(cat "$JS_FILE" | sed "s|{{VERSION}}|$VERSION|g")
# Inject version and favicon into JS
JS_CONTENT=$(cat "$JS_FILE" | sed "s|{{VERSION}}|$VERSION|g" | sed "s|{{FAVICON_DATA_URI}}|$FAVICON_URL|g")
# === 1. Build standalone HTML (for local testing/dev) ===
cat > "$DIST_DIR/kantine-standalone.html" << HTMLEOF
@@ -107,7 +107,7 @@ cat > "$DIST_DIR/install.html" << INSTALLEOF
<head>
<meta charset="UTF-8">
<title>Kantine Wrapper Installer ($VERSION)</title>
<link rel="icon" type="image/svg+xml" href="$FAVICON_URI">
<link rel="icon" type="image/png" href="$FAVICON_URL">
<style>
body { font-family: 'Inter', sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; background: #1a1a2e; color: #eee; }
h1 { color: #029AA8; } /* Knapp Teal */
@@ -207,9 +207,9 @@ echo "document.getElementById('bookmarklet-link').href = " >> "$DIST_DIR/install
echo "$JS_CONTENT" | python3 -c "
import sys, json, urllib.parse
# 1. Read JS and Replace VERSION
# 1. Read JS and Replace VERSION + Favicon
js_template = sys.stdin.read()
js = js_template.replace('{{VERSION}}', '$VERSION')
js = js_template.replace('{{VERSION}}', '$VERSION').replace('{{FAVICON_DATA_URI}}', '$FAVICON_URL')
# 2. Prepare CSS for injection via createElement('style')
css = open('$CSS_FILE').read().replace('\n', ' ').replace(' ', ' ')
@@ -243,6 +243,16 @@ $CHANGELOG_HTML
EOF
cat >> "$DIST_DIR/install.html" << INSTALLEOF
// Dynamic favicon injection — setTimeout ensures it runs AFTER
// htmlpreview.github.io's document.write() processing completes
setTimeout(function() {
document.querySelectorAll('link[rel*="icon"]').forEach(function(el) { el.remove(); });
var fi = document.createElement('link');
fi.rel = 'icon';
fi.type = 'image/png';
fi.href = '$FAVICON_URL';
document.head.appendChild(fi);
}, 0);
document.getElementById('bookmarklet-link').textContent = 'Kantine $VERSION';
</script>
</body>

View File

@@ -1,79 +1,16 @@
## v1.4.24
- 🐛 **Bugfix**: Favicon-Encoding korrigiert `encodeURIComponent` verursachte doppeltes Encoding der Farbcodes (`%23``%2523`). Auf Base64 umgestellt, funktioniert nun auch unter Chrome/Windows.
## v1.5.0 (2026-02-26)
Das große "Quality of Life"-Update! Zusammenfassung aller Features und Fixes seit v1.4.0:
## v1.4.23
- 🐛 **Bugfix**: Favicon wird jetzt beim Ausführen des Bookmarklets in die Seite injiziert (ersetzt das Bessa-Favicon im Tab). Chrome cached das Icon und übernimmt es anschließend auch in die Lesezeichenleiste.
-**Bestellhistorie**: Übersichtliche Historie direkt in der App gruppiert nach Jahr/Monat, inklusive Summen, Stati (Offen/Abgeschlossen/Storniert) und Delta-Cache für rasantes Laden.
- **Smart Cache & Performance**: Massive Reduzierung von API-Calls und Ladezeiten durch intelligenten lokalen Cache. Das Bookmarklet startet nun praktisch verzögerungsfrei.
- 🔄 **GitHub Release Management**: In-App Versions-Menü mit Auto-Update Check (`🆕` Icon). Umschalten zwischen "Stable" und "Dev" Versionen sowie Downgrade-Möglichkeit direkt über die GitHub API.
- 🌟 **Smart Highlights & UX**: Speisen-Favoriten leuchten nun im Design-Violett und erhalten Feature-Badges. Der Bestell-Badge für nächste Woche filtert nun intelligent personalisierte Highlights voraus.
- 🔔 **Bestell-Warnung & Notifications**: Der System-Alarm berücksichtigt nun Sessions korrekt, zeigt dynamische Farbwechsel (gelb/grün/rot) und warnt verlässlich vor dem Bestellschluss (10:00 Uhr). Altlasten von Vortagen werden automatisch geputzt.
- 🎨 **Eigenes Favicon**: Das Bookmarklet und der Installer haben nun ein eigenes Icon (Dreieck mit Besteck), das beim Hineinziehen in die Lesezeichenleiste übernommen wird (dynamisch generiert als lokales PNG).
- 🧹 **Lokaler Cache-Clear**: Ein in das Versions-Menü eingebauter "Papierkorb", der ausschließlich fehlerhafte Kantinen-Caches putzt, ohne dabei versehentlich die aktive Bessa-Host-Session zu zerstören.
- 🔒 **Sitzungs-Persistenz**: Die Login-Session überdauert jetzt neue Tabs, Fenster und Version-Upgrades reibungslos durch den Wechsel auf `localStorage`.
- 🛡️ **Testing & Stabilität**: Vollautomatische DOM- und Logik-Testing-Suites in der Release-Pipeline integriert. Fehlerhafte UI-Buttons gehören der Vergangenheit an.
## v1.4.22
- 📝 **Docs**: Vollständiger Dokumentations-Audit: README.md um fehlende Dateien (favicon.svg, release.sh, Tests) und Features (Bestellhistorie, Version-Menü, Cache leeren, Favicon) ergänzt. REQUIREMENTS.md: Doppelte IDs (FR-090/091) behoben, FR-092 an dynamische Glow-Logik angepasst, FR-116 (Cache leeren) und FR-117 (Favicon) hinzugefügt, NFR-008 um DOM-Tests erweitert.
## v1.4.21
-**UX**: Der Glow-Effekt des „Nächste Woche"-Buttons bleibt nun aktiv, solange Menüdaten vorhanden aber noch keine Bestellungen getätigt wurden. Verschwindet automatisch nach der ersten Bestellung.
## v1.4.20
- 🐛 **Bugfix**: Der Badge-Counter im „Nächste Woche"-Tab wird jetzt sofort nach einer Bestellung oder Stornierung aktualisiert.
## v1.4.19
- 🎨 **Feature**: Eigenes Favicon für die Installer-Seite hinzugefügt (Dreieck + Gabel & Messer). Wird beim Drag & Drop in die Lesezeichenleiste als Icon übernommen.
## v1.4.18
- 🧪 **Testing**: Die automatische DOM-Testing Suite (`test_dom.js`) wurde massiv ausgebaut. Sie prüft nun neben der Alarmglocke und den Highlights auch systematisch alle anderen UI-Komponenten (Login-Modal, History-Modal, Versionen-Modal, Theme-Toggle, und Navigation Tabs) auf korrekte Event-Listener-Bindungen, um Regressionen (tote Buttons) endgültig auszuschließen.
## v1.4.17
- 🐛 **Bugfix**: Regression behoben: Der "Persönliche Highlights" (Stern-Button) Dialog öffnet sich nun wieder korrekt.
- 🧪 **Testing**: Es wurde ein initialer UI-Testing-Hook (`test_dom.js` mit `jsdom`) in die Build-Pipeline integriert, um kritische DOM Event-Listener Regressionen (wie den Highlights-Button und die Alarmglocke) automatisch zu preventen.
## v1.4.16
-**Feature**: Ein Button "Lokalen Cache leeren" wurde zum Versionen-Menü hinzugefügt, um bei hartnäckigen lokalen Fehlern alle Caches und Sessions bereinigen zu können, ohne die Entwicklertools (F12) des Browsers bemühen zu müssen.
## v1.4.15
- 🧹 **Bugfix**: In der Vergangenheit gesetzte Alarme/Flags wurden nicht zuverlässig gelöscht. Dies ist nun behoben, sodass verfallene Menüs nach 10:00 Uhr bzw. an vergangenen Tagen automatisch aus dem Tracker verschwinden.
## v1.4.14
- 🐛 **Bugfix**: Alarmglocke versteckt sich jetzt zuverlässig auch auf Endgeräten mit CSS Konflikten
- 🚀 **Feature**: Sofortige API-Aktualisierung (Refresh) bei Aktivierung eines Menüalarms
-**Optimierung**: "Unbekannt" im letzten Refresh-Zeitpunkt wird abgefangen und zeigt initial "gerade eben"
## v1.4.13 (2026-02-24)
- **Fix**: Die Farben der Glocke funktionieren nun verlässlich, da CSS-Variablen durch direkte Hex-Codes ersetzt wurden.
## v1.4.12 (2026-02-24)
- **Fix**: Das Glocken-Icon sollte nun endgültig versteckt bleiben, wenn keine Benachrichtigungen aktiv sind (CSS-Kollision mit `.hidden` behoben).
## v1.4.11 (2026-02-24)
- **Feature**: Das Versionsmenü prüft nun im Hintergrund direkt beim Öffnen nach neuen Versionen und aktualisiert die Liste automatisch, selbst wenn eine veraltete Liste noch im Cache liegt.
## v1.4.10 (2026-02-24)
- **Fix**: Die Farben der Benachrichtigungs-Glocke wurden korrigiert: Sie ist nun gelb, während man auf ein Menü wartet, und wird grün, sobald eines verfügbar ist.
## v1.4.9 (2026-02-24)
- **Fix**: Das Glocken-Icon für Benachrichtigungen wird nun direkt beim Start (wenn Daten aus dem lokalen Cache geladen werden) korrekt angezeigt.
## v1.4.8 (2026-02-24)
- **Fix**: Die Benachrichtigungs-Glocke wird nun korrekt in Gelb dargestellt, wenn beobachtete Menüs verfügbar sind.
- **Tools**: Fehler in Testskript behoben, der den CI/CD Build verlangsamt hat.
## v1.4.7 (2026-02-24)
- **Performance**: Die Bestellhistorie nutzt nun einen inkrementellen Delta-Cache anstatt immer alle Seiten von der API herunterzuladen, was die Ladezeiten für Vielbesteller enorm reduziert.
## v1.4.6 (2026-02-24)
- **Fix**: Die Umrandung für bereits bestellte Menüs der vergangenen Tage ist nun ebenfalls einheitlich violett statt blau.
## v1.4.5 (2026-02-24)
- **Fix**: Doppelten Scrollbalken in der Versionen-Liste entfernt.
## v1.4.4 (2026-02-24)
- **Feature**: Das Versionsmenü enthält nun direkte Links zu GitHub, um Fehler zu melden oder neue Features vorzuschlagen.
## v1.4.3 (2026-02-24)
- **Fix**: Der Rahmen des "Heute Bestellt" Menüs ist nun konsequent violett (passend zum Glow-Effekt).
## v1.4.2 (2026-02-23)
- **Fix**: Das "Heute Bestellt" Menü leuchtet nun stimmig im Design-Violett statt Blau.
- **Fix**: Abfangen des GitHub API Rate Limit (403) im Versionsdialog mit einer freundlicheren Fehlermeldung, da der User-Agent im Browser nicht manuell gesetzt werden darf.
## v1.4.1 (2026-02-22)
- **UX Verbesserungen**: Bestellhistorie gruppiert nach Jahren und Monaten mittels einklappbarem Akkordeon. Monatssummen integriert und Stati farblich abgehoben (Offen, Abgeschlossen, Storniert).
## v1.4.0 (2026-02-22)
- **Feature**: Bestellhistorie per Knopfdruck abrufbar. Übersichtliche Darstellung, gruppiert nach Monaten und Kalenderwochen, inklusive Stornos. 📜✨

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

135
dist/install.html vendored

File diff suppressed because one or more lines are too long

View File

@@ -1979,8 +1979,8 @@ body {
let currentWeekNumber = getISOWeek(new Date());
let currentYear = new Date().getFullYear();
let displayMode = 'this-week';
let authToken = sessionStorage.getItem('kantine_authToken');
let currentUser = sessionStorage.getItem('kantine_currentUser');
let authToken = localStorage.getItem('kantine_authToken');
let currentUser = localStorage.getItem('kantine_currentUser');
let orderMap = new Map();
let userFlags = new Set(JSON.parse(localStorage.getItem('kantine_flags') || '[]'));
let pollIntervalId = null;
@@ -2000,15 +2000,14 @@ body {
// Replace entire page content
document.title = 'Kantine Weekly Menu';
// Inject custom favicon (triangle + fork & knife)
// Inject custom favicon (triangle + fork & knife PNG)
if (document.querySelectorAll) {
document.querySelectorAll('link[rel*="icon"]').forEach(el => el.remove());
}
const favicon = document.createElement('link');
favicon.rel = 'icon';
favicon.type = 'image/svg+xml';
// Pre-encoded Base64 of the SVG favicon (triangle + fork & knife)
favicon.href = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMiwxMCkiPjxyZWN0IHg9IjEiIHk9IjAiIHdpZHRoPSIxLjgiIGhlaWdodD0iMTYiIHJ4PSIuOSIgZmlsbD0iIzMzMyIvPjxyZWN0IHg9IjQuNiIgeT0iMCIgd2lkdGg9IjEuOCIgaGVpZ2h0PSIxNiIgcng9Ii45IiBmaWxsPSIjMzMzIi8+PHJlY3QgeD0iOC4yIiB5PSIwIiB3aWR0aD0iMS44IiBoZWlnaHQ9IjE2IiByeD0iLjkiIGZpbGw9IiMzMzMiLz48cmVjdCB4PSIxIiB5PSIxNCIgd2lkdGg9IjkiIGhlaWdodD0iMy41IiByeD0iMS41IiBmaWxsPSIjMzMzIi8+PHJlY3QgeD0iMy41IiB5PSIxNi41IiB3aWR0aD0iNCIgaGVpZ2h0PSIyNCIgcng9IjIiIGZpbGw9IiMzMzMiLz48L2c+PHBvbHlnb24gcG9pbnRzPSIzMiw4IDQ3LDQ4IDE3LDQ4IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUwLDEwKSI+PHBhdGggZD0iTTMsMEMzLDAsMywwLDMsMEwzLDE3TDEwLDE0QzEwLDYsNywwLDMsMFoiIGZpbGw9IiMzMzMiLz48cmVjdCB4PSIxLjUiIHk9IjAiIHdpZHRoPSIyIiBoZWlnaHQ9IjE4IiByeD0iMSIgZmlsbD0iIzMzMyIvPjxyZWN0IHg9IjEuNSIgeT0iMTYuNSIgd2lkdGg9IjguNSIgaGVpZ2h0PSIzLjUiIHJ4PSIxLjIiIGZpbGw9IiMzMzMiLz48cmVjdCB4PSIzLjUiIHk9IjE5IiB3aWR0aD0iNCIgaGVpZ2h0PSIyMiIgcng9IjIiIGZpbGw9IiMzMzMiLz48L2c+PC9zdmc+';
favicon.type = 'image/png';
favicon.href = 'data:image/png;base64,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';
document.head.appendChild(favicon);
// Inject Google Fonts if not already present
@@ -2032,7 +2031,7 @@ body {
<div class="brand">
<span class="material-icons-round logo-icon">restaurant_menu</span>
<div class="header-left">
<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.24</small></h1>
<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.5.0</small></h1>
<div id="last-updated-subtitle" class="subtitle"></div>
</div>
<div class="nav-group" style="margin-left: 1rem;">
@@ -2174,7 +2173,7 @@ body {
</div>
<div class="modal-body">
<div style="margin-bottom: 1rem;">
<strong>Aktuell:</strong> <span id="version-current">v1.4.24</span>
<strong>Aktuell:</strong> <span id="version-current">v1.5.0</span>
</div>
<div class="dev-toggle">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
@@ -2295,8 +2294,12 @@ body {
if (btnClearCache) {
btnClearCache.addEventListener('click', () => {
if (confirm('Möchtest du wirklich alle lokalen Daten (inkl. Login-Session, Cache und Einstellungen) löschen? Die Seite wird danach neu geladen.')) {
localStorage.clear();
sessionStorage.clear();
// Only clear our own keys so we don't destroy the host app's (Bessa's) session
Object.keys(localStorage).forEach(key => {
if (key.startsWith('kantine_')) {
localStorage.removeItem(key);
}
});
window.location.reload();
}
});
@@ -2410,8 +2413,8 @@ body {
if (response.ok) {
authToken = data.key;
currentUser = employeeId;
sessionStorage.setItem('kantine_authToken', data.key);
sessionStorage.setItem('kantine_currentUser', employeeId);
localStorage.setItem('kantine_authToken', data.key);
localStorage.setItem('kantine_currentUser', employeeId);
// Fetch user name
try {
@@ -2420,8 +2423,8 @@ body {
});
if (userResp.ok) {
const userData = await userResp.json();
if (userData.first_name) sessionStorage.setItem('kantine_firstName', userData.first_name);
if (userData.last_name) sessionStorage.setItem('kantine_lastName', userData.last_name);
if (userData.first_name) localStorage.setItem('kantine_firstName', userData.first_name);
if (userData.last_name) localStorage.setItem('kantine_lastName', userData.last_name);
}
} catch (err) {
console.error('Failed to fetch user info:', err);
@@ -2451,10 +2454,10 @@ body {
// Logout
btnLogout.addEventListener('click', () => {
sessionStorage.removeItem('kantine_authToken');
sessionStorage.removeItem('kantine_currentUser');
sessionStorage.removeItem('kantine_firstName');
sessionStorage.removeItem('kantine_lastName');
localStorage.removeItem('kantine_authToken');
localStorage.removeItem('kantine_currentUser');
localStorage.removeItem('kantine_firstName');
localStorage.removeItem('kantine_lastName');
authToken = null;
currentUser = null;
orderMap = new Map();
@@ -2475,13 +2478,13 @@ body {
if (parsed.auth && parsed.auth.token) {
console.log('Found existing Bessa session!');
authToken = parsed.auth.token;
sessionStorage.setItem('kantine_authToken', authToken);
localStorage.setItem('kantine_authToken', authToken);
if (parsed.auth.user) {
currentUser = parsed.auth.user.id || 'unknown';
sessionStorage.setItem('kantine_currentUser', currentUser);
if (parsed.auth.user.firstName) sessionStorage.setItem('kantine_firstName', parsed.auth.user.firstName);
if (parsed.auth.user.lastName) sessionStorage.setItem('kantine_lastName', parsed.auth.user.lastName);
localStorage.setItem('kantine_currentUser', currentUser);
if (parsed.auth.user.firstName) localStorage.setItem('kantine_firstName', parsed.auth.user.firstName);
if (parsed.auth.user.lastName) localStorage.setItem('kantine_lastName', parsed.auth.user.lastName);
}
}
}
@@ -2490,9 +2493,9 @@ body {
}
}
authToken = sessionStorage.getItem('kantine_authToken');
currentUser = sessionStorage.getItem('kantine_currentUser');
const firstName = sessionStorage.getItem('kantine_firstName');
authToken = localStorage.getItem('kantine_authToken');
currentUser = localStorage.getItem('kantine_currentUser');
const firstName = localStorage.getItem('kantine_firstName');
const btnLoginOpen = document.getElementById('btn-login-open');
const userInfo = document.getElementById('user-info');
const userIdDisplay = document.getElementById('user-id-display');
@@ -4027,7 +4030,7 @@ body {
// Periodic update check (runs on init + every hour)
async function checkForUpdates() {
const currentVersion = 'v1.4.24';
const currentVersion = 'v1.5.0';
const devMode = localStorage.getItem('kantine_dev_mode') === 'true';
try {
@@ -4068,7 +4071,7 @@ body {
const modal = document.getElementById('version-modal');
const container = document.getElementById('version-list-container');
const devToggle = document.getElementById('dev-mode-toggle');
const currentVersion = 'v1.4.24';
const currentVersion = 'v1.5.0';
if (!modal) return;
modal.classList.remove('hidden');
@@ -4165,6 +4168,12 @@ body {
// === Order Countdown ===
function updateCountdown() {
// Only show order alarms for logged-in users
if (!authToken || !currentUser) {
removeCountdown();
return;
}
const now = new Date();
const currentDay = now.getDay();
// Skip weekends (0=Sun, 6=Sat)
@@ -4229,7 +4238,7 @@ body {
// Notification logic (One time)
const notifiedKey = `kantine_notified_${todayStr}`;
if (!sessionStorage.getItem(notifiedKey)) {
if (!localStorage.getItem(notifiedKey)) {
if (Notification.permission === 'granted') {
new Notification('Kantine: Bestellschluss naht!', {
body: 'Du hast heute noch nichts bestellt. Nur noch 1 Stunde!',
@@ -4238,7 +4247,7 @@ body {
} else if (Notification.permission === 'default') {
Notification.requestPermission();
}
sessionStorage.setItem(notifiedKey, 'true');
localStorage.setItem(notifiedKey, 'true');
}
} else {
countdownEl.classList.remove('urgent');

BIN
favicon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
favicon_base.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

View File

@@ -29,8 +29,8 @@
let currentWeekNumber = getISOWeek(new Date());
let currentYear = new Date().getFullYear();
let displayMode = 'this-week';
let authToken = sessionStorage.getItem('kantine_authToken');
let currentUser = sessionStorage.getItem('kantine_currentUser');
let authToken = localStorage.getItem('kantine_authToken');
let currentUser = localStorage.getItem('kantine_currentUser');
let orderMap = new Map();
let userFlags = new Set(JSON.parse(localStorage.getItem('kantine_flags') || '[]'));
let pollIntervalId = null;
@@ -50,15 +50,14 @@
// Replace entire page content
document.title = 'Kantine Weekly Menu';
// Inject custom favicon (triangle + fork & knife)
// Inject custom favicon (triangle + fork & knife PNG)
if (document.querySelectorAll) {
document.querySelectorAll('link[rel*="icon"]').forEach(el => el.remove());
}
const favicon = document.createElement('link');
favicon.rel = 'icon';
favicon.type = 'image/svg+xml';
// Pre-encoded Base64 of the SVG favicon (triangle + fork & knife)
favicon.href = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMiwxMCkiPjxyZWN0IHg9IjEiIHk9IjAiIHdpZHRoPSIxLjgiIGhlaWdodD0iMTYiIHJ4PSIuOSIgZmlsbD0iIzMzMyIvPjxyZWN0IHg9IjQuNiIgeT0iMCIgd2lkdGg9IjEuOCIgaGVpZ2h0PSIxNiIgcng9Ii45IiBmaWxsPSIjMzMzIi8+PHJlY3QgeD0iOC4yIiB5PSIwIiB3aWR0aD0iMS44IiBoZWlnaHQ9IjE2IiByeD0iLjkiIGZpbGw9IiMzMzMiLz48cmVjdCB4PSIxIiB5PSIxNCIgd2lkdGg9IjkiIGhlaWdodD0iMy41IiByeD0iMS41IiBmaWxsPSIjMzMzIi8+PHJlY3QgeD0iMy41IiB5PSIxNi41IiB3aWR0aD0iNCIgaGVpZ2h0PSIyNCIgcng9IjIiIGZpbGw9IiMzMzMiLz48L2c+PHBvbHlnb24gcG9pbnRzPSIzMiw4IDQ3LDQ4IDE3LDQ4IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUwLDEwKSI+PHBhdGggZD0iTTMsMEMzLDAsMywwLDMsMEwzLDE3TDEwLDE0QzEwLDYsNywwLDMsMFoiIGZpbGw9IiMzMzMiLz48cmVjdCB4PSIxLjUiIHk9IjAiIHdpZHRoPSIyIiBoZWlnaHQ9IjE4IiByeD0iMSIgZmlsbD0iIzMzMyIvPjxyZWN0IHg9IjEuNSIgeT0iMTYuNSIgd2lkdGg9IjguNSIgaGVpZ2h0PSIzLjUiIHJ4PSIxLjIiIGZpbGw9IiMzMzMiLz48cmVjdCB4PSIzLjUiIHk9IjE5IiB3aWR0aD0iNCIgaGVpZ2h0PSIyMiIgcng9IjIiIGZpbGw9IiMzMzMiLz48L2c+PC9zdmc+';
favicon.type = 'image/png';
favicon.href = '{{FAVICON_DATA_URI}}';
document.head.appendChild(favicon);
// Inject Google Fonts if not already present
@@ -345,8 +344,12 @@
if (btnClearCache) {
btnClearCache.addEventListener('click', () => {
if (confirm('Möchtest du wirklich alle lokalen Daten (inkl. Login-Session, Cache und Einstellungen) löschen? Die Seite wird danach neu geladen.')) {
localStorage.clear();
sessionStorage.clear();
// Only clear our own keys so we don't destroy the host app's (Bessa's) session
Object.keys(localStorage).forEach(key => {
if (key.startsWith('kantine_')) {
localStorage.removeItem(key);
}
});
window.location.reload();
}
});
@@ -460,8 +463,8 @@
if (response.ok) {
authToken = data.key;
currentUser = employeeId;
sessionStorage.setItem('kantine_authToken', data.key);
sessionStorage.setItem('kantine_currentUser', employeeId);
localStorage.setItem('kantine_authToken', data.key);
localStorage.setItem('kantine_currentUser', employeeId);
// Fetch user name
try {
@@ -470,8 +473,8 @@
});
if (userResp.ok) {
const userData = await userResp.json();
if (userData.first_name) sessionStorage.setItem('kantine_firstName', userData.first_name);
if (userData.last_name) sessionStorage.setItem('kantine_lastName', userData.last_name);
if (userData.first_name) localStorage.setItem('kantine_firstName', userData.first_name);
if (userData.last_name) localStorage.setItem('kantine_lastName', userData.last_name);
}
} catch (err) {
console.error('Failed to fetch user info:', err);
@@ -501,10 +504,10 @@
// Logout
btnLogout.addEventListener('click', () => {
sessionStorage.removeItem('kantine_authToken');
sessionStorage.removeItem('kantine_currentUser');
sessionStorage.removeItem('kantine_firstName');
sessionStorage.removeItem('kantine_lastName');
localStorage.removeItem('kantine_authToken');
localStorage.removeItem('kantine_currentUser');
localStorage.removeItem('kantine_firstName');
localStorage.removeItem('kantine_lastName');
authToken = null;
currentUser = null;
orderMap = new Map();
@@ -525,13 +528,13 @@
if (parsed.auth && parsed.auth.token) {
console.log('Found existing Bessa session!');
authToken = parsed.auth.token;
sessionStorage.setItem('kantine_authToken', authToken);
localStorage.setItem('kantine_authToken', authToken);
if (parsed.auth.user) {
currentUser = parsed.auth.user.id || 'unknown';
sessionStorage.setItem('kantine_currentUser', currentUser);
if (parsed.auth.user.firstName) sessionStorage.setItem('kantine_firstName', parsed.auth.user.firstName);
if (parsed.auth.user.lastName) sessionStorage.setItem('kantine_lastName', parsed.auth.user.lastName);
localStorage.setItem('kantine_currentUser', currentUser);
if (parsed.auth.user.firstName) localStorage.setItem('kantine_firstName', parsed.auth.user.firstName);
if (parsed.auth.user.lastName) localStorage.setItem('kantine_lastName', parsed.auth.user.lastName);
}
}
}
@@ -540,9 +543,9 @@
}
}
authToken = sessionStorage.getItem('kantine_authToken');
currentUser = sessionStorage.getItem('kantine_currentUser');
const firstName = sessionStorage.getItem('kantine_firstName');
authToken = localStorage.getItem('kantine_authToken');
currentUser = localStorage.getItem('kantine_currentUser');
const firstName = localStorage.getItem('kantine_firstName');
const btnLoginOpen = document.getElementById('btn-login-open');
const userInfo = document.getElementById('user-info');
const userIdDisplay = document.getElementById('user-id-display');
@@ -2215,6 +2218,12 @@
// === Order Countdown ===
function updateCountdown() {
// Only show order alarms for logged-in users
if (!authToken || !currentUser) {
removeCountdown();
return;
}
const now = new Date();
const currentDay = now.getDay();
// Skip weekends (0=Sun, 6=Sat)
@@ -2279,7 +2288,7 @@
// Notification logic (One time)
const notifiedKey = `kantine_notified_${todayStr}`;
if (!sessionStorage.getItem(notifiedKey)) {
if (!localStorage.getItem(notifiedKey)) {
if (Notification.permission === 'granted') {
new Notification('Kantine: Bestellschluss naht!', {
body: 'Du hast heute noch nichts bestellt. Nur noch 1 Stunde!',
@@ -2288,7 +2297,7 @@
} else if (Notification.permission === 'default') {
Notification.requestPermission();
}
sessionStorage.setItem(notifiedKey, 'true');
localStorage.setItem(notifiedKey, 'true');
}
} else {
countdownEl.classList.remove('urgent');

View File

@@ -1 +1 @@
v1.4.24
v1.5.0