feat: immediate api refresh on flag, fix timestamp fallback (v1.4.14)

This commit is contained in:
Kantine Wrapper
2026-02-24 15:31:48 +01:00
parent fd765a74c0
commit d895a5fb7c
8 changed files with 237 additions and 29 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

18
dist/install.html vendored

File diff suppressed because one or more lines are too long

View File

@@ -2021,7 +2021,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.13</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.14</small></h1>
<div id="last-updated-subtitle" class="subtitle"></div>
</div>
<div class="nav-group" style="margin-left: 1rem;">
@@ -2163,7 +2163,7 @@ body {
</div>
<div class="modal-body">
<div style="margin-bottom: 1rem;">
<strong>Aktuell:</strong> <span id="version-current">v1.4.13</span>
<strong>Aktuell:</strong> <span id="version-current">v1.4.14</span>
</div>
<div class="dev-toggle">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
@@ -2889,6 +2889,67 @@ body {
localStorage.setItem('kantine_flags', JSON.stringify([...userFlags]));
}
async function refreshFlaggedItems() {
if (userFlags.size === 0) return;
const token = authToken || GUEST_TOKEN;
const datesToFetch = new Set();
for (const flagId of userFlags) {
const [dateStr] = flagId.split('_');
datesToFetch.add(dateStr);
}
let updated = false;
for (const dateStr of datesToFetch) {
try {
const resp = await fetch(`${API_BASE}/venues/${VENUE_ID}/menu/${MENU_ID}/${dateStr}/`, {
headers: apiHeaders(token)
});
if (!resp.ok) continue;
const data = await resp.json();
const menuGroups = data.results || [];
let dayItems = [];
for (const group of menuGroups) {
if (group.items && Array.isArray(group.items)) {
dayItems = dayItems.concat(group.items);
}
}
// Update allWeeks in memory
for (let week of allWeeks) {
if (!week.days) continue;
let dayObj = week.days.find(d => d.date === dateStr);
if (dayObj) {
dayObj.items = dayItems.map(item => {
const isUnlimited = item.amount_tracking === false;
const hasStock = parseInt(item.available_amount) > 0;
return {
id: `${dateStr}_${item.id}`,
articleId: item.id,
name: item.name || 'Unknown',
description: item.description || '',
price: parseFloat(item.price) || 0,
available: isUnlimited || hasStock,
availableAmount: parseInt(item.available_amount) || 0,
amountTracking: item.amount_tracking !== false
};
});
updated = true;
}
}
} catch (e) {
console.error('Error refreshing flag date', dateStr, e);
}
}
if (updated) {
saveMenuCache();
updateLastUpdatedTime(new Date().toISOString());
updateAlarmBell();
renderVisibleWeeks();
}
}
function updateAlarmBell() {
const bellBtn = document.getElementById('alarm-bell');
const bellIcon = document.getElementById('alarm-bell-icon');
@@ -2896,12 +2957,14 @@ body {
if (userFlags.size === 0) {
bellBtn.classList.add('hidden');
bellBtn.style.display = 'none';
bellIcon.style.color = 'var(--text-secondary)';
bellIcon.style.textShadow = 'none';
return;
}
bellBtn.classList.remove('hidden');
bellBtn.style.display = 'inline-flex';
// Check if any flagged item is available
let anyAvailable = false;
@@ -2920,15 +2983,20 @@ body {
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.`;
let lastUpdatedStr = localStorage.getItem('kantine_last_updated');
let timeStr = 'gerade eben'; // Fallback instead of Unbekannt
if (!lastUpdatedStr) {
lastUpdatedStr = new Date().toISOString();
localStorage.setItem('kantine_last_updated', lastUpdatedStr);
}
const lastUpdated = new Date(lastUpdatedStr);
const diffMs = Date.now() - lastUpdated.getTime();
const diffMins = Math.floor(diffMs / 60000);
if (diffMins < 1) timeStr = 'gerade eben';
else if (diffMins < 60) timeStr = `vor ${diffMins} Min.`;
else timeStr = `vor ${Math.floor(diffMins / 60)} Std.`;
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
if (anyAvailable) {
@@ -2942,11 +3010,13 @@ body {
function toggleFlag(date, articleId, name, cutoff) {
const id = `${date}_${articleId}`;
let flagAdded = false;
if (userFlags.has(id)) {
userFlags.delete(id);
showToast(`Flag entfernt für ${name}`, 'success');
} else {
userFlags.add(id);
flagAdded = true;
showToast(`Benachrichtigung aktiviert für ${name}`, 'success');
if (Notification.permission === 'default') {
Notification.requestPermission();
@@ -2955,6 +3025,10 @@ body {
saveFlags();
updateAlarmBell();
renderVisibleWeeks();
if (flagAdded) {
refreshFlaggedItems();
}
}
// FR-019: Auto-remove flags whose cutoff has passed
@@ -3895,7 +3969,7 @@ body {
// Periodic update check (runs on init + every hour)
async function checkForUpdates() {
const currentVersion = 'v1.4.13';
const currentVersion = 'v1.4.14';
const devMode = localStorage.getItem('kantine_dev_mode') === 'true';
try {
@@ -3936,7 +4010,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.13';
const currentVersion = 'v1.4.14';
if (!modal) return;
modal.classList.remove('hidden');