Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
984a897f73 | ||
|
|
ae54d97d96 | ||
|
|
6ed0831f5d | ||
|
|
ba75544f68 | ||
|
|
7fdf7f6f3e | ||
|
|
614f498d11 | ||
|
|
5f30696315 | ||
|
|
cb5aa28f94 | ||
|
|
bc1a91b7d7 | ||
|
|
7d5beedfbb | ||
|
|
0651d517b2 | ||
|
|
c5e236e095 | ||
|
|
a5bff19796 | ||
|
|
284f3d9a32 | ||
|
|
7ce82ce82e | ||
|
|
ce12684193 | ||
|
|
6cee38e99f | ||
|
|
88758427fd | ||
|
|
23ed867ac4 | ||
|
|
f8b1334a9a |
@@ -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>
|
||||
|
||||
28
changelog.md
28
changelog.md
@@ -1,3 +1,31 @@
|
||||
## v1.4.31
|
||||
- 🐛 **Bugfix**: Der "Lokalen Cache leeren"-Button löscht nun gezielt nur noch Kantine-spezifische Daten (`kantine_` Präfix). Bisher wurde die gesamte `localStorage` geleert, was dazu führte, dass man auch aus der zugrundeliegenden bessa.app-Sitzung ausgeloggt wurde.
|
||||
|
||||
## 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
|
||||
- 🐛 **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.
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
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
61
dist/install.html
vendored
61
dist/install.html
vendored
File diff suppressed because one or more lines are too long
70
dist/kantine-standalone.html
vendored
70
dist/kantine-standalone.html
vendored
@@ -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,6 +2000,16 @@ body {
|
||||
// Replace entire page content
|
||||
document.title = 'Kantine Weekly Menu';
|
||||
|
||||
// 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/png';
|
||||
favicon.href = 'data:image/png;base64,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';
|
||||
document.head.appendChild(favicon);
|
||||
|
||||
// Inject Google Fonts if not already present
|
||||
if (!document.querySelector('link[href*="fonts.googleapis.com/css2?family=Inter"]')) {
|
||||
const fontLink = document.createElement('link');
|
||||
@@ -2021,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.22</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.31</small></h1>
|
||||
<div id="last-updated-subtitle" class="subtitle"></div>
|
||||
</div>
|
||||
<div class="nav-group" style="margin-left: 1rem;">
|
||||
@@ -2163,7 +2173,7 @@ body {
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<strong>Aktuell:</strong> <span id="version-current">v1.4.22</span>
|
||||
<strong>Aktuell:</strong> <span id="version-current">v1.4.31</span>
|
||||
</div>
|
||||
<div class="dev-toggle">
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
|
||||
@@ -2284,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();
|
||||
}
|
||||
});
|
||||
@@ -2399,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 {
|
||||
@@ -2409,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);
|
||||
@@ -2440,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();
|
||||
@@ -2464,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2479,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');
|
||||
@@ -4016,7 +4030,7 @@ body {
|
||||
|
||||
// Periodic update check (runs on init + every hour)
|
||||
async function checkForUpdates() {
|
||||
const currentVersion = 'v1.4.22';
|
||||
const currentVersion = 'v1.4.31';
|
||||
const devMode = localStorage.getItem('kantine_dev_mode') === 'true';
|
||||
|
||||
try {
|
||||
@@ -4057,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.22';
|
||||
const currentVersion = 'v1.4.31';
|
||||
|
||||
if (!modal) return;
|
||||
modal.classList.remove('hidden');
|
||||
@@ -4154,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)
|
||||
@@ -4218,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!',
|
||||
@@ -4227,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
BIN
favicon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
favicon_base.png
Executable file
BIN
favicon_base.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 MiB |
62
kantine.js
62
kantine.js
@@ -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,6 +50,16 @@
|
||||
// Replace entire page content
|
||||
document.title = 'Kantine Weekly Menu';
|
||||
|
||||
// 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/png';
|
||||
favicon.href = '{{FAVICON_DATA_URI}}';
|
||||
document.head.appendChild(favicon);
|
||||
|
||||
// Inject Google Fonts if not already present
|
||||
if (!document.querySelector('link[href*="fonts.googleapis.com/css2?family=Inter"]')) {
|
||||
const fontLink = document.createElement('link');
|
||||
@@ -334,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();
|
||||
}
|
||||
});
|
||||
@@ -449,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 {
|
||||
@@ -459,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);
|
||||
@@ -490,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();
|
||||
@@ -514,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -529,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');
|
||||
@@ -2204,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)
|
||||
@@ -2268,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!',
|
||||
@@ -2277,7 +2297,7 @@
|
||||
} else if (Notification.permission === 'default') {
|
||||
Notification.requestPermission();
|
||||
}
|
||||
sessionStorage.setItem(notifiedKey, 'true');
|
||||
localStorage.setItem(notifiedKey, 'true');
|
||||
}
|
||||
} else {
|
||||
countdownEl.classList.remove('urgent');
|
||||
|
||||
@@ -49,8 +49,9 @@ echo "=== Pushing to remotes ==="
|
||||
git push origin HEAD
|
||||
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
|
||||
git push github HEAD
|
||||
git push github --force tag "$VERSION"
|
||||
fi
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.4.22
|
||||
v1.4.31
|
||||
|
||||
Reference in New Issue
Block a user