feat: Add manual refresh for flagged items triggered by the alarm bell, including UI feedback and toast notifications.
This commit is contained in:
@@ -61,6 +61,7 @@ Das System umfasst die Darstellung von Menüplänen in einer Wochenübersicht, d
|
||||
| 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 aber kein Menü verfügbar, Grün=Mindestens ein 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.6.11 (Update v1.5.0) |
|
||||
| FR-092 | Solange Menüdaten für die Nächste Woche verfügbar sind, aber noch keine Bestellungen getätigt wurden, muss der entsprechende Navigation-Button animiert und farblich (Gelb) hervorgehoben werden. Nach der ersten Bestellung muss die Hervorhebung automatisch erlöschen. Zusätzlich muss beim erstmaligen Erscheinen der Daten ein einmaliger Toast-Hinweis angezeigt werden. | Mittel | v1.6.0 (Update v1.4.21) |
|
||||
| FR-093 | Das System muss dem Benutzer ermöglichen, durch Klicken auf das Alarm-Icon im Header eine manuelle Prüfung der geflaggten Menüs auszulösen. Während der Prüfung muss das Icon visuell animiert sein (Rotation). Nach Abschluss der Prüfung muss eine Toast-Nachricht mit der Anzahl der geprüften Menüs angezeigt werden. | Mittel | v1.6.13 |
|
||||
| **Sprachfilter** | | | |
|
||||
| FR-120 | Das System muss zweisprachige Menübeschreibungen (Deutsch/Englisch) erkennen und dem Benutzer erlauben, via UI-Toggle zwischen DE, EN und ALL (beide Sprachen) zu wechseln. Die Sprachpräferenz muss persistent gespeichert werden. Allergen-Codes müssen in allen Modi angezeigt werden. | Mittel | v1.6.0 |
|
||||
| FR-121 | Das System muss bei fehlenden Übersetzungen in zweisprachigen Menüs robust reagieren. Wenn ein Gang nur in einer Sprache vorliegt, muss dieser Teil für beide Sprachansichten herangezogen werden, um die Konsistenz der Ganganzahl zu gewährleisten. | Mittel | v1.6.10 |
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
## v1.6.14 (2026-03-10)
|
||||
- 🐛 **Bugfix**: Die globale "Aktualisiert am"-Zeit im Header wird bei einer manuellen Prüfung der geflaggten Menüs nicht mehr zurückgesetzt.
|
||||
|
||||
## v1.6.13 (2026-03-10)
|
||||
- ✨ **Feature**: Manueller Refresh der geflaggten Menüs durch Klick auf das Alarm-Icon im Header ([FR-093](REQUIREMENTS.md#FR-093)).
|
||||
- 🔄 **UI**: Visuelle Rückmeldung während der Prüfung durch Rotation des Icons.
|
||||
- 🔔 **Notification**: Toast-Benachrichtigung zeigt die Anzahl der geprüften Menüs an.
|
||||
|
||||
## v1.6.12 (2026-03-10)
|
||||
- 🔄 **Refactor**: Modularisierung von `kantine.js` in ES6-Module (`api.js`, `state.js`, `utils.js`, `ui.js`, etc.).
|
||||
- 📦 **Build**: Integration von Webpack in den Build-Prozess zur Unterstützung der modularen Struktur.
|
||||
|
||||
4
dist/bookmarklet-payload.js
vendored
4
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
36
dist/install.html
vendored
36
dist/install.html
vendored
File diff suppressed because one or more lines are too long
138
dist/kantine-standalone.html
vendored
138
dist/kantine-standalone.html
vendored
File diff suppressed because one or more lines are too long
27
dist/kantine.bundle.js
vendored
27
dist/kantine.bundle.js
vendored
@@ -6,6 +6,7 @@
|
||||
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ A0: () => (/* binding */ refreshFlaggedItems),
|
||||
/* harmony export */ Aq: () => (/* binding */ fetchFullOrderHistory),
|
||||
/* harmony export */ BM: () => (/* binding */ checkHighlight),
|
||||
/* harmony export */ Et: () => (/* binding */ stopPolling),
|
||||
@@ -23,7 +24,7 @@
|
||||
/* harmony export */ oL: () => (/* binding */ addHighlightTag),
|
||||
/* harmony export */ wH: () => (/* binding */ placeOrder)
|
||||
/* harmony export */ });
|
||||
/* unused harmony exports renderHistory, saveFlags, refreshFlaggedItems, pollFlaggedItems, saveHighlightTags, removeHighlightTag, saveMenuCache, updateLastUpdatedTime */
|
||||
/* unused harmony exports renderHistory, saveFlags, pollFlaggedItems, saveHighlightTags, removeHighlightTag, saveMenuCache, updateLastUpdatedTime */
|
||||
/* harmony import */ var _state_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(901);
|
||||
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(413);
|
||||
/* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(521);
|
||||
@@ -470,6 +471,10 @@ async function refreshFlaggedItems() {
|
||||
}
|
||||
|
||||
let updated = false;
|
||||
const bellBtn = document.getElementById('alarm-bell');
|
||||
if (bellBtn) bellBtn.classList.add('refreshing');
|
||||
|
||||
try {
|
||||
for (const dateStr of datesToFetch) {
|
||||
try {
|
||||
const resp = await fetch(`${_constants_js__WEBPACK_IMPORTED_MODULE_2__/* .API_BASE */ .tE}/venues/${_constants_js__WEBPACK_IMPORTED_MODULE_2__/* .VENUE_ID */ .eW}/menu/${_constants_js__WEBPACK_IMPORTED_MODULE_2__/* .MENU_ID */ .YU}/${dateStr}/`, {
|
||||
@@ -513,11 +518,15 @@ async function refreshFlaggedItems() {
|
||||
|
||||
if (updated) {
|
||||
saveMenuCache();
|
||||
updateLastUpdatedTime(new Date().toISOString());
|
||||
localStorage.setItem('kantine_flagged_items_last_checked', new Date().toISOString());
|
||||
(0,_ui_helpers_js__WEBPACK_IMPORTED_MODULE_4__/* .updateAlarmBell */ .Mb)();
|
||||
(0,_ui_helpers_js__WEBPACK_IMPORTED_MODULE_4__/* .renderVisibleWeeks */ .OR)();
|
||||
}
|
||||
|
||||
showToast(`${_state_js__WEBPACK_IMPORTED_MODULE_0__/* .userFlags */ .BY.size} ${_state_js__WEBPACK_IMPORTED_MODULE_0__/* .userFlags */ .BY.size === 1 ? 'Menü' : 'Menüs'} geprüft`, 'info');
|
||||
} finally {
|
||||
if (bellBtn) bellBtn.classList.remove('refreshing');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1351,7 +1360,7 @@ function createDayCard(day) {
|
||||
header.className = 'card-header';
|
||||
const dateStr = cardDate.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' });
|
||||
|
||||
const badgesHtml = menuBadges.map(code => `<span class="menu-code-badge">${code}</span>`).join('');
|
||||
const badgesHtml = menuBadges.reduce((acc, code) => acc + `<span class="menu-code-badge">${code}</span>`, '');
|
||||
|
||||
let headerClass = '';
|
||||
const hasAnyOrder = day.items && day.items.some(item => {
|
||||
@@ -1467,10 +1476,7 @@ function createDayCard(day) {
|
||||
|
||||
let tagsHtml = '';
|
||||
if (matchedTags.length > 0) {
|
||||
let badges = '';
|
||||
for (const t of matchedTags) {
|
||||
badges += `<span class="tag-badge-small"><span class="material-icons-round" style="font-size:10px;margin-right:2px">star</span>${(0,_utils_js__WEBPACK_IMPORTED_MODULE_1__/* .escapeHtml */ .ZD)(t)}</span>`;
|
||||
}
|
||||
const badges = matchedTags.reduce((acc, t) => acc + `<span class="tag-badge-small"><span class="material-icons-round" style="font-size:10px;margin-right:2px">star</span>${(0,_utils_js__WEBPACK_IMPORTED_MODULE_1__/* .escapeHtml */ .ZD)(t)}</span>`, '');
|
||||
tagsHtml = `<div class="matched-tags">${badges}</div>`;
|
||||
}
|
||||
|
||||
@@ -2578,6 +2584,13 @@ function bindEvents() {
|
||||
(0,actions/* loadMenuDataFromAPI */.m9)();
|
||||
});
|
||||
|
||||
const bellBtn = document.getElementById('alarm-bell');
|
||||
if (bellBtn) {
|
||||
bellBtn.addEventListener('click', () => {
|
||||
(0,actions/* refreshFlaggedItems */.A0)();
|
||||
});
|
||||
}
|
||||
|
||||
btnLoginOpen.addEventListener('click', () => {
|
||||
loginModal.classList.remove('hidden');
|
||||
document.getElementById('login-error').classList.add('hidden');
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "app",
|
||||
"name": "kantine-wrapper",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -439,6 +439,10 @@ export async function refreshFlaggedItems() {
|
||||
}
|
||||
|
||||
let updated = false;
|
||||
const bellBtn = document.getElementById('alarm-bell');
|
||||
if (bellBtn) bellBtn.classList.add('refreshing');
|
||||
|
||||
try {
|
||||
for (const dateStr of datesToFetch) {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE}/venues/${VENUE_ID}/menu/${MENU_ID}/${dateStr}/`, {
|
||||
@@ -482,11 +486,15 @@ export async function refreshFlaggedItems() {
|
||||
|
||||
if (updated) {
|
||||
saveMenuCache();
|
||||
updateLastUpdatedTime(new Date().toISOString());
|
||||
localStorage.setItem('kantine_flagged_items_last_checked', new Date().toISOString());
|
||||
updateAlarmBell();
|
||||
renderVisibleWeeks();
|
||||
}
|
||||
|
||||
showToast(`${userFlags.size} ${userFlags.size === 1 ? 'Menü' : 'Menüs'} geprüft`, 'info');
|
||||
} finally {
|
||||
if (bellBtn) bellBtn.classList.remove('refreshing');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { displayMode, langMode, authToken, currentUser, orderMap, userFlags, pollIntervalId, setLangMode, setDisplayMode, setAuthToken, setCurrentUser, setOrderMap } from './state.js';
|
||||
import { updateAuthUI, loadMenuDataFromAPI, fetchOrders, startPolling, stopPolling, fetchFullOrderHistory, addHighlightTag, renderTagsList } from './actions.js';
|
||||
import { updateAuthUI, loadMenuDataFromAPI, fetchOrders, startPolling, stopPolling, fetchFullOrderHistory, addHighlightTag, renderTagsList, refreshFlaggedItems } from './actions.js';
|
||||
import { renderVisibleWeeks, openVersionMenu } from './ui_helpers.js';
|
||||
import { API_BASE, GUEST_TOKEN } from './constants.js';
|
||||
import { apiHeaders } from './api.js';
|
||||
@@ -162,6 +162,13 @@ export function bindEvents() {
|
||||
loadMenuDataFromAPI();
|
||||
});
|
||||
|
||||
const bellBtn = document.getElementById('alarm-bell');
|
||||
if (bellBtn) {
|
||||
bellBtn.addEventListener('click', () => {
|
||||
refreshFlaggedItems();
|
||||
});
|
||||
}
|
||||
|
||||
btnLoginOpen.addEventListener('click', () => {
|
||||
loginModal.classList.remove('hidden');
|
||||
document.getElementById('login-error').classList.add('hidden');
|
||||
|
||||
@@ -352,7 +352,8 @@ body {
|
||||
}
|
||||
|
||||
/* Refresh button animation */
|
||||
#btn-refresh.refreshing .material-icons-round {
|
||||
#btn-refresh.refreshing .material-icons-round,
|
||||
#alarm-bell.refreshing .material-icons-round {
|
||||
animation: rotate 1s linear infinite;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,9 @@ const testCode = `
|
||||
alarmBtn.classList.add('hidden');
|
||||
if (!document.getElementById('alarm-bell').className.includes('hidden')) throw new Error("Bell should be hidden");
|
||||
|
||||
console.log("✅ Alarm Bell Test Passed");
|
||||
// Test Click Refresh
|
||||
alarmBtn.click();
|
||||
console.log("✅ Alarm Bell Test (Click) Passed");
|
||||
|
||||
console.log("--- Testing Highlights Modal ---");
|
||||
// First, verify initial state
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.6.12
|
||||
v1.6.14
|
||||
|
||||
Reference in New Issue
Block a user