Compare commits

...

16 Commits

Author SHA1 Message Date
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
Kantine Wrapper
7ce82ce82e chore: update build artifacts for v1.4.24 2026-02-26 08:58:38 +01:00
Kantine Wrapper
ce12684193 fix: push main branch to GitHub in release script 2026-02-26 08:58:25 +01:00
Kantine Wrapper
6cee38e99f chore: update build artifacts for v1.4.24 2026-02-26 08:35:15 +01:00
Kantine Wrapper
88758427fd fix: favicon base64 encoding for Chrome/Windows compatibility (v1.4.24) 2026-02-26 08:35:09 +01:00
11 changed files with 151 additions and 69 deletions

View File

@@ -7,7 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DIST_DIR="$SCRIPT_DIR/dist" DIST_DIR="$SCRIPT_DIR/dist"
CSS_FILE="$SCRIPT_DIR/style.css" CSS_FILE="$SCRIPT_DIR/style.css"
JS_FILE="$SCRIPT_DIR/kantine.js" JS_FILE="$SCRIPT_DIR/kantine.js"
FAVICON_FILE="$SCRIPT_DIR/favicon.svg" FAVICON_FILE="$SCRIPT_DIR/favicon.png"
# === VERSION === # === VERSION ===
if [ -f "$SCRIPT_DIR/version.txt" ]; then 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 "$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 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_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") CSS_CONTENT=$(cat "$CSS_FILE")
# Inject version into JS # Inject version and favicon into JS
JS_CONTENT=$(cat "$JS_FILE" | sed "s|{{VERSION}}|$VERSION|g") 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) === # === 1. Build standalone HTML (for local testing/dev) ===
cat > "$DIST_DIR/kantine-standalone.html" << HTMLEOF cat > "$DIST_DIR/kantine-standalone.html" << HTMLEOF
@@ -107,7 +107,7 @@ cat > "$DIST_DIR/install.html" << INSTALLEOF
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Kantine Wrapper Installer ($VERSION)</title> <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> <style>
body { font-family: 'Inter', sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; background: #1a1a2e; color: #eee; } body { font-family: 'Inter', sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; background: #1a1a2e; color: #eee; }
h1 { color: #029AA8; } /* Knapp Teal */ h1 { color: #029AA8; } /* Knapp Teal */
@@ -207,9 +207,9 @@ echo "document.getElementById('bookmarklet-link').href = " >> "$DIST_DIR/install
echo "$JS_CONTENT" | python3 -c " echo "$JS_CONTENT" | python3 -c "
import sys, json, urllib.parse import sys, json, urllib.parse
# 1. Read JS and Replace VERSION # 1. Read JS and Replace VERSION + Favicon
js_template = sys.stdin.read() 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') # 2. Prepare CSS for injection via createElement('style')
css = open('$CSS_FILE').read().replace('\n', ' ').replace(' ', ' ') css = open('$CSS_FILE').read().replace('\n', ' ').replace(' ', ' ')
@@ -243,6 +243,16 @@ $CHANGELOG_HTML
EOF EOF
cat >> "$DIST_DIR/install.html" << INSTALLEOF 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'; document.getElementById('bookmarklet-link').textContent = 'Kantine $VERSION';
</script> </script>
</body> </body>

View File

@@ -1,3 +1,25 @@
## v1.4.30
- 🐛 **Bugfix**: Login-Sitzung (`authToken` etc.) wird nun in der `localStorage` statt `sessionStorage` gespeichert, wodurch die Anmeldung beim Öffnen von Bookmarklets in neuen Tabs/Fenstern erhalten bleibt.
- 🐛 **Bugfix**: Bestell-Erinnerungscountdown und Alarm-Notifications erscheinen nun nur noch für angemeldete Nutzer.
## v1.4.29
- 🐛 **Bugfix**: Favicon-Injection in `install.html` mit `setTimeout(0)` verzögert, sodass sie nach dem `document.write()` von htmlpreview.github.io läuft. Chrome erkennt Favicon-Änderungen erst im nächsten Event-Loop-Tick.
## v1.4.28
- 🎨 **Favicon**: Eigenes Favicon-Design aus `favicon_base.png` (2048x2048) auf 32x32 skaliert. Wird beim Build automatisch als PNG-Data-URI in Bookmarklet und Installer injiziert.
## v1.4.27
- 🔧 **Build**: Favicon wird jetzt sauber aus `favicon.png` per Build-Script als PNG-Base64-Data-URI generiert und über `{{FAVICON_DATA_URI}}` Platzhalter in `kantine.js` + `install.html` injiziert. Funktioniert auf allen Browsern und Proxy-Diensten.
## v1.4.26
- 🎨 **Favicon**: Von SVG-Base64 auf PNG-Datei (`favicon.png`) umgestellt, verlinkt via raw.githubusercontent.com. Funktioniert zuverlässig auf allen Browsern und Proxy-Diensten (htmlpreview.github.io).
## v1.4.25
- 🐛 **Bugfix**: Favicon wird in `install.html` jetzt zusätzlich per JavaScript injiziert, um Proxy-Dienste wie htmlpreview.github.io zu überschreiben. Release-Script pusht nun auch `main` Branch zu GitHub.
## 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.4.23 ## 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. - 🐛 **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.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

51
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 currentWeekNumber = getISOWeek(new Date());
let currentYear = new Date().getFullYear(); let currentYear = new Date().getFullYear();
let displayMode = 'this-week'; let displayMode = 'this-week';
let authToken = sessionStorage.getItem('kantine_authToken'); let authToken = localStorage.getItem('kantine_authToken');
let currentUser = sessionStorage.getItem('kantine_currentUser'); let currentUser = localStorage.getItem('kantine_currentUser');
let orderMap = new Map(); let orderMap = new Map();
let userFlags = new Set(JSON.parse(localStorage.getItem('kantine_flags') || '[]')); let userFlags = new Set(JSON.parse(localStorage.getItem('kantine_flags') || '[]'));
let pollIntervalId = null; let pollIntervalId = null;
@@ -2000,14 +2000,14 @@ body {
// Replace entire page content // Replace entire page content
document.title = 'Kantine Weekly Menu'; document.title = 'Kantine Weekly Menu';
// Inject custom favicon (triangle + fork & knife) // Inject custom favicon (triangle + fork & knife PNG)
if (document.querySelectorAll) { if (document.querySelectorAll) {
document.querySelectorAll('link[rel*="icon"]').forEach(el => el.remove()); document.querySelectorAll('link[rel*="icon"]').forEach(el => el.remove());
} }
const favicon = document.createElement('link'); const favicon = document.createElement('link');
favicon.rel = 'icon'; favicon.rel = 'icon';
favicon.type = 'image/svg+xml'; favicon.type = 'image/png';
favicon.href = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><g transform="translate(2,10)"><rect x="1" y="0" width="1.8" height="16" rx=".9" fill="%23333"/><rect x="4.6" y="0" width="1.8" height="16" rx=".9" fill="%23333"/><rect x="8.2" y="0" width="1.8" height="16" rx=".9" fill="%23333"/><rect x="1" y="14" width="9" height="3.5" rx="1.5" fill="%23333"/><rect x="3.5" y="16.5" width="4" height="24" rx="2" fill="%23333"/></g><polygon points="32,8 47,48 17,48" fill="none" stroke="%23333" stroke-width="4" stroke-linejoin="round"/><g transform="translate(50,10)"><path d="M3,0C3,0,3,0,3,0L3,17L10,14C10,6,7,0,3,0Z" fill="%23333"/><rect x="1.5" y="0" width="2" height="18" rx="1" fill="%23333"/><rect x="1.5" y="16.5" width="8.5" height="3.5" rx="1.2" fill="%23333"/><rect x="3.5" y="19" width="4" height="22" rx="2" fill="%23333"/></g></svg>'); favicon.href = 'data:image/png;base64,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';
document.head.appendChild(favicon); document.head.appendChild(favicon);
// Inject Google Fonts if not already present // Inject Google Fonts if not already present
@@ -2031,7 +2031,7 @@ body {
<div class="brand"> <div class="brand">
<span class="material-icons-round logo-icon">restaurant_menu</span> <span class="material-icons-round logo-icon">restaurant_menu</span>
<div class="header-left"> <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.23</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.4.30</small></h1>
<div id="last-updated-subtitle" class="subtitle"></div> <div id="last-updated-subtitle" class="subtitle"></div>
</div> </div>
<div class="nav-group" style="margin-left: 1rem;"> <div class="nav-group" style="margin-left: 1rem;">
@@ -2173,7 +2173,7 @@ body {
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div style="margin-bottom: 1rem;"> <div style="margin-bottom: 1rem;">
<strong>Aktuell:</strong> <span id="version-current">v1.4.23</span> <strong>Aktuell:</strong> <span id="version-current">v1.4.30</span>
</div> </div>
<div class="dev-toggle"> <div class="dev-toggle">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;"> <label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
@@ -2295,7 +2295,6 @@ body {
btnClearCache.addEventListener('click', () => { 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.')) { 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(); localStorage.clear();
sessionStorage.clear();
window.location.reload(); window.location.reload();
} }
}); });
@@ -2409,8 +2408,8 @@ body {
if (response.ok) { if (response.ok) {
authToken = data.key; authToken = data.key;
currentUser = employeeId; currentUser = employeeId;
sessionStorage.setItem('kantine_authToken', data.key); localStorage.setItem('kantine_authToken', data.key);
sessionStorage.setItem('kantine_currentUser', employeeId); localStorage.setItem('kantine_currentUser', employeeId);
// Fetch user name // Fetch user name
try { try {
@@ -2419,8 +2418,8 @@ body {
}); });
if (userResp.ok) { if (userResp.ok) {
const userData = await userResp.json(); const userData = await userResp.json();
if (userData.first_name) sessionStorage.setItem('kantine_firstName', userData.first_name); if (userData.first_name) localStorage.setItem('kantine_firstName', userData.first_name);
if (userData.last_name) sessionStorage.setItem('kantine_lastName', userData.last_name); if (userData.last_name) localStorage.setItem('kantine_lastName', userData.last_name);
} }
} catch (err) { } catch (err) {
console.error('Failed to fetch user info:', err); console.error('Failed to fetch user info:', err);
@@ -2450,10 +2449,10 @@ body {
// Logout // Logout
btnLogout.addEventListener('click', () => { btnLogout.addEventListener('click', () => {
sessionStorage.removeItem('kantine_authToken'); localStorage.removeItem('kantine_authToken');
sessionStorage.removeItem('kantine_currentUser'); localStorage.removeItem('kantine_currentUser');
sessionStorage.removeItem('kantine_firstName'); localStorage.removeItem('kantine_firstName');
sessionStorage.removeItem('kantine_lastName'); localStorage.removeItem('kantine_lastName');
authToken = null; authToken = null;
currentUser = null; currentUser = null;
orderMap = new Map(); orderMap = new Map();
@@ -2474,13 +2473,13 @@ body {
if (parsed.auth && parsed.auth.token) { if (parsed.auth && parsed.auth.token) {
console.log('Found existing Bessa session!'); console.log('Found existing Bessa session!');
authToken = parsed.auth.token; authToken = parsed.auth.token;
sessionStorage.setItem('kantine_authToken', authToken); localStorage.setItem('kantine_authToken', authToken);
if (parsed.auth.user) { if (parsed.auth.user) {
currentUser = parsed.auth.user.id || 'unknown'; currentUser = parsed.auth.user.id || 'unknown';
sessionStorage.setItem('kantine_currentUser', currentUser); localStorage.setItem('kantine_currentUser', currentUser);
if (parsed.auth.user.firstName) sessionStorage.setItem('kantine_firstName', parsed.auth.user.firstName); if (parsed.auth.user.firstName) localStorage.setItem('kantine_firstName', parsed.auth.user.firstName);
if (parsed.auth.user.lastName) sessionStorage.setItem('kantine_lastName', parsed.auth.user.lastName); if (parsed.auth.user.lastName) localStorage.setItem('kantine_lastName', parsed.auth.user.lastName);
} }
} }
} }
@@ -2489,9 +2488,9 @@ body {
} }
} }
authToken = sessionStorage.getItem('kantine_authToken'); authToken = localStorage.getItem('kantine_authToken');
currentUser = sessionStorage.getItem('kantine_currentUser'); currentUser = localStorage.getItem('kantine_currentUser');
const firstName = sessionStorage.getItem('kantine_firstName'); const firstName = localStorage.getItem('kantine_firstName');
const btnLoginOpen = document.getElementById('btn-login-open'); const btnLoginOpen = document.getElementById('btn-login-open');
const userInfo = document.getElementById('user-info'); const userInfo = document.getElementById('user-info');
const userIdDisplay = document.getElementById('user-id-display'); const userIdDisplay = document.getElementById('user-id-display');
@@ -4026,7 +4025,7 @@ body {
// Periodic update check (runs on init + every hour) // Periodic update check (runs on init + every hour)
async function checkForUpdates() { async function checkForUpdates() {
const currentVersion = 'v1.4.23'; const currentVersion = 'v1.4.30';
const devMode = localStorage.getItem('kantine_dev_mode') === 'true'; const devMode = localStorage.getItem('kantine_dev_mode') === 'true';
try { try {
@@ -4067,7 +4066,7 @@ body {
const modal = document.getElementById('version-modal'); const modal = document.getElementById('version-modal');
const container = document.getElementById('version-list-container'); const container = document.getElementById('version-list-container');
const devToggle = document.getElementById('dev-mode-toggle'); const devToggle = document.getElementById('dev-mode-toggle');
const currentVersion = 'v1.4.23'; const currentVersion = 'v1.4.30';
if (!modal) return; if (!modal) return;
modal.classList.remove('hidden'); modal.classList.remove('hidden');
@@ -4164,6 +4163,12 @@ body {
// === Order Countdown === // === Order Countdown ===
function updateCountdown() { function updateCountdown() {
// Only show order alarms for logged-in users
if (!authToken || !currentUser) {
removeCountdown();
return;
}
const now = new Date(); const now = new Date();
const currentDay = now.getDay(); const currentDay = now.getDay();
// Skip weekends (0=Sun, 6=Sat) // Skip weekends (0=Sun, 6=Sat)
@@ -4228,7 +4233,7 @@ body {
// Notification logic (One time) // Notification logic (One time)
const notifiedKey = `kantine_notified_${todayStr}`; const notifiedKey = `kantine_notified_${todayStr}`;
if (!sessionStorage.getItem(notifiedKey)) { if (!localStorage.getItem(notifiedKey)) {
if (Notification.permission === 'granted') { if (Notification.permission === 'granted') {
new Notification('Kantine: Bestellschluss naht!', { new Notification('Kantine: Bestellschluss naht!', {
body: 'Du hast heute noch nichts bestellt. Nur noch 1 Stunde!', body: 'Du hast heute noch nichts bestellt. Nur noch 1 Stunde!',
@@ -4237,7 +4242,7 @@ body {
} else if (Notification.permission === 'default') { } else if (Notification.permission === 'default') {
Notification.requestPermission(); Notification.requestPermission();
} }
sessionStorage.setItem(notifiedKey, 'true'); localStorage.setItem(notifiedKey, 'true');
} }
} else { } else {
countdownEl.classList.remove('urgent'); 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 currentWeekNumber = getISOWeek(new Date());
let currentYear = new Date().getFullYear(); let currentYear = new Date().getFullYear();
let displayMode = 'this-week'; let displayMode = 'this-week';
let authToken = sessionStorage.getItem('kantine_authToken'); let authToken = localStorage.getItem('kantine_authToken');
let currentUser = sessionStorage.getItem('kantine_currentUser'); let currentUser = localStorage.getItem('kantine_currentUser');
let orderMap = new Map(); let orderMap = new Map();
let userFlags = new Set(JSON.parse(localStorage.getItem('kantine_flags') || '[]')); let userFlags = new Set(JSON.parse(localStorage.getItem('kantine_flags') || '[]'));
let pollIntervalId = null; let pollIntervalId = null;
@@ -50,14 +50,14 @@
// Replace entire page content // Replace entire page content
document.title = 'Kantine Weekly Menu'; document.title = 'Kantine Weekly Menu';
// Inject custom favicon (triangle + fork & knife) // Inject custom favicon (triangle + fork & knife PNG)
if (document.querySelectorAll) { if (document.querySelectorAll) {
document.querySelectorAll('link[rel*="icon"]').forEach(el => el.remove()); document.querySelectorAll('link[rel*="icon"]').forEach(el => el.remove());
} }
const favicon = document.createElement('link'); const favicon = document.createElement('link');
favicon.rel = 'icon'; favicon.rel = 'icon';
favicon.type = 'image/svg+xml'; favicon.type = 'image/png';
favicon.href = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><g transform="translate(2,10)"><rect x="1" y="0" width="1.8" height="16" rx=".9" fill="%23333"/><rect x="4.6" y="0" width="1.8" height="16" rx=".9" fill="%23333"/><rect x="8.2" y="0" width="1.8" height="16" rx=".9" fill="%23333"/><rect x="1" y="14" width="9" height="3.5" rx="1.5" fill="%23333"/><rect x="3.5" y="16.5" width="4" height="24" rx="2" fill="%23333"/></g><polygon points="32,8 47,48 17,48" fill="none" stroke="%23333" stroke-width="4" stroke-linejoin="round"/><g transform="translate(50,10)"><path d="M3,0C3,0,3,0,3,0L3,17L10,14C10,6,7,0,3,0Z" fill="%23333"/><rect x="1.5" y="0" width="2" height="18" rx="1" fill="%23333"/><rect x="1.5" y="16.5" width="8.5" height="3.5" rx="1.2" fill="%23333"/><rect x="3.5" y="19" width="4" height="22" rx="2" fill="%23333"/></g></svg>'); favicon.href = '{{FAVICON_DATA_URI}}';
document.head.appendChild(favicon); document.head.appendChild(favicon);
// Inject Google Fonts if not already present // Inject Google Fonts if not already present
@@ -345,7 +345,6 @@
btnClearCache.addEventListener('click', () => { 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.')) { 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(); localStorage.clear();
sessionStorage.clear();
window.location.reload(); window.location.reload();
} }
}); });
@@ -459,8 +458,8 @@
if (response.ok) { if (response.ok) {
authToken = data.key; authToken = data.key;
currentUser = employeeId; currentUser = employeeId;
sessionStorage.setItem('kantine_authToken', data.key); localStorage.setItem('kantine_authToken', data.key);
sessionStorage.setItem('kantine_currentUser', employeeId); localStorage.setItem('kantine_currentUser', employeeId);
// Fetch user name // Fetch user name
try { try {
@@ -469,8 +468,8 @@
}); });
if (userResp.ok) { if (userResp.ok) {
const userData = await userResp.json(); const userData = await userResp.json();
if (userData.first_name) sessionStorage.setItem('kantine_firstName', userData.first_name); if (userData.first_name) localStorage.setItem('kantine_firstName', userData.first_name);
if (userData.last_name) sessionStorage.setItem('kantine_lastName', userData.last_name); if (userData.last_name) localStorage.setItem('kantine_lastName', userData.last_name);
} }
} catch (err) { } catch (err) {
console.error('Failed to fetch user info:', err); console.error('Failed to fetch user info:', err);
@@ -500,10 +499,10 @@
// Logout // Logout
btnLogout.addEventListener('click', () => { btnLogout.addEventListener('click', () => {
sessionStorage.removeItem('kantine_authToken'); localStorage.removeItem('kantine_authToken');
sessionStorage.removeItem('kantine_currentUser'); localStorage.removeItem('kantine_currentUser');
sessionStorage.removeItem('kantine_firstName'); localStorage.removeItem('kantine_firstName');
sessionStorage.removeItem('kantine_lastName'); localStorage.removeItem('kantine_lastName');
authToken = null; authToken = null;
currentUser = null; currentUser = null;
orderMap = new Map(); orderMap = new Map();
@@ -524,13 +523,13 @@
if (parsed.auth && parsed.auth.token) { if (parsed.auth && parsed.auth.token) {
console.log('Found existing Bessa session!'); console.log('Found existing Bessa session!');
authToken = parsed.auth.token; authToken = parsed.auth.token;
sessionStorage.setItem('kantine_authToken', authToken); localStorage.setItem('kantine_authToken', authToken);
if (parsed.auth.user) { if (parsed.auth.user) {
currentUser = parsed.auth.user.id || 'unknown'; currentUser = parsed.auth.user.id || 'unknown';
sessionStorage.setItem('kantine_currentUser', currentUser); localStorage.setItem('kantine_currentUser', currentUser);
if (parsed.auth.user.firstName) sessionStorage.setItem('kantine_firstName', parsed.auth.user.firstName); if (parsed.auth.user.firstName) localStorage.setItem('kantine_firstName', parsed.auth.user.firstName);
if (parsed.auth.user.lastName) sessionStorage.setItem('kantine_lastName', parsed.auth.user.lastName); if (parsed.auth.user.lastName) localStorage.setItem('kantine_lastName', parsed.auth.user.lastName);
} }
} }
} }
@@ -539,9 +538,9 @@
} }
} }
authToken = sessionStorage.getItem('kantine_authToken'); authToken = localStorage.getItem('kantine_authToken');
currentUser = sessionStorage.getItem('kantine_currentUser'); currentUser = localStorage.getItem('kantine_currentUser');
const firstName = sessionStorage.getItem('kantine_firstName'); const firstName = localStorage.getItem('kantine_firstName');
const btnLoginOpen = document.getElementById('btn-login-open'); const btnLoginOpen = document.getElementById('btn-login-open');
const userInfo = document.getElementById('user-info'); const userInfo = document.getElementById('user-info');
const userIdDisplay = document.getElementById('user-id-display'); const userIdDisplay = document.getElementById('user-id-display');
@@ -2214,6 +2213,12 @@
// === Order Countdown === // === Order Countdown ===
function updateCountdown() { function updateCountdown() {
// Only show order alarms for logged-in users
if (!authToken || !currentUser) {
removeCountdown();
return;
}
const now = new Date(); const now = new Date();
const currentDay = now.getDay(); const currentDay = now.getDay();
// Skip weekends (0=Sun, 6=Sat) // Skip weekends (0=Sun, 6=Sat)
@@ -2278,7 +2283,7 @@
// Notification logic (One time) // Notification logic (One time)
const notifiedKey = `kantine_notified_${todayStr}`; const notifiedKey = `kantine_notified_${todayStr}`;
if (!sessionStorage.getItem(notifiedKey)) { if (!localStorage.getItem(notifiedKey)) {
if (Notification.permission === 'granted') { if (Notification.permission === 'granted') {
new Notification('Kantine: Bestellschluss naht!', { new Notification('Kantine: Bestellschluss naht!', {
body: 'Du hast heute noch nichts bestellt. Nur noch 1 Stunde!', body: 'Du hast heute noch nichts bestellt. Nur noch 1 Stunde!',
@@ -2287,7 +2292,7 @@
} else if (Notification.permission === 'default') { } else if (Notification.permission === 'default') {
Notification.requestPermission(); Notification.requestPermission();
} }
sessionStorage.setItem(notifiedKey, 'true'); localStorage.setItem(notifiedKey, 'true');
} }
} else { } else {
countdownEl.classList.remove('urgent'); countdownEl.classList.remove('urgent');

View File

@@ -49,8 +49,9 @@ echo "=== Pushing to remotes ==="
git push origin HEAD git push origin HEAD
git push origin --force tag "$VERSION" git push origin --force tag "$VERSION"
# If a remote named 'github' exists, push tags there too # If a remote named 'github' exists, push branch and tags there too
if git remote | grep -q "^github$"; then if git remote | grep -q "^github$"; then
git push github HEAD
git push github --force tag "$VERSION" git push github --force tag "$VERSION"
fi fi

View File

@@ -1 +1 @@
v1.4.23 v1.4.30