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-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-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-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** | | | |
|
| **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-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 |
|
| 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)
|
## v1.6.12 (2026-03-10)
|
||||||
- 🔄 **Refactor**: Modularisierung von `kantine.js` in ES6-Module (`api.js`, `state.js`, `utils.js`, `ui.js`, etc.).
|
- 🔄 **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.
|
- 📦 **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
34
dist/install.html
vendored
34
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__) {
|
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||||
|
/* harmony export */ A0: () => (/* binding */ refreshFlaggedItems),
|
||||||
/* harmony export */ Aq: () => (/* binding */ fetchFullOrderHistory),
|
/* harmony export */ Aq: () => (/* binding */ fetchFullOrderHistory),
|
||||||
/* harmony export */ BM: () => (/* binding */ checkHighlight),
|
/* harmony export */ BM: () => (/* binding */ checkHighlight),
|
||||||
/* harmony export */ Et: () => (/* binding */ stopPolling),
|
/* harmony export */ Et: () => (/* binding */ stopPolling),
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
/* harmony export */ oL: () => (/* binding */ addHighlightTag),
|
/* harmony export */ oL: () => (/* binding */ addHighlightTag),
|
||||||
/* harmony export */ wH: () => (/* binding */ placeOrder)
|
/* harmony export */ wH: () => (/* binding */ placeOrder)
|
||||||
/* harmony export */ });
|
/* 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 _state_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(901);
|
||||||
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(413);
|
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(413);
|
||||||
/* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(521);
|
/* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(521);
|
||||||
@@ -470,6 +471,10 @@ async function refreshFlaggedItems() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let updated = false;
|
let updated = false;
|
||||||
|
const bellBtn = document.getElementById('alarm-bell');
|
||||||
|
if (bellBtn) bellBtn.classList.add('refreshing');
|
||||||
|
|
||||||
|
try {
|
||||||
for (const dateStr of datesToFetch) {
|
for (const dateStr of datesToFetch) {
|
||||||
try {
|
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}/`, {
|
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) {
|
if (updated) {
|
||||||
saveMenuCache();
|
saveMenuCache();
|
||||||
updateLastUpdatedTime(new Date().toISOString());
|
|
||||||
localStorage.setItem('kantine_flagged_items_last_checked', 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__/* .updateAlarmBell */ .Mb)();
|
||||||
(0,_ui_helpers_js__WEBPACK_IMPORTED_MODULE_4__/* .renderVisibleWeeks */ .OR)();
|
(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';
|
header.className = 'card-header';
|
||||||
const dateStr = cardDate.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' });
|
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 = '';
|
let headerClass = '';
|
||||||
const hasAnyOrder = day.items && day.items.some(item => {
|
const hasAnyOrder = day.items && day.items.some(item => {
|
||||||
@@ -1467,10 +1476,7 @@ function createDayCard(day) {
|
|||||||
|
|
||||||
let tagsHtml = '';
|
let tagsHtml = '';
|
||||||
if (matchedTags.length > 0) {
|
if (matchedTags.length > 0) {
|
||||||
let badges = '';
|
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>`, '');
|
||||||
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>`;
|
|
||||||
}
|
|
||||||
tagsHtml = `<div class="matched-tags">${badges}</div>`;
|
tagsHtml = `<div class="matched-tags">${badges}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2578,6 +2584,13 @@ function bindEvents() {
|
|||||||
(0,actions/* loadMenuDataFromAPI */.m9)();
|
(0,actions/* loadMenuDataFromAPI */.m9)();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const bellBtn = document.getElementById('alarm-bell');
|
||||||
|
if (bellBtn) {
|
||||||
|
bellBtn.addEventListener('click', () => {
|
||||||
|
(0,actions/* refreshFlaggedItems */.A0)();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
btnLoginOpen.addEventListener('click', () => {
|
btnLoginOpen.addEventListener('click', () => {
|
||||||
loginModal.classList.remove('hidden');
|
loginModal.classList.remove('hidden');
|
||||||
document.getElementById('login-error').classList.add('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,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|||||||
@@ -439,6 +439,10 @@ export async function refreshFlaggedItems() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let updated = false;
|
let updated = false;
|
||||||
|
const bellBtn = document.getElementById('alarm-bell');
|
||||||
|
if (bellBtn) bellBtn.classList.add('refreshing');
|
||||||
|
|
||||||
|
try {
|
||||||
for (const dateStr of datesToFetch) {
|
for (const dateStr of datesToFetch) {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`${API_BASE}/venues/${VENUE_ID}/menu/${MENU_ID}/${dateStr}/`, {
|
const resp = await fetch(`${API_BASE}/venues/${VENUE_ID}/menu/${MENU_ID}/${dateStr}/`, {
|
||||||
@@ -482,11 +486,15 @@ export async function refreshFlaggedItems() {
|
|||||||
|
|
||||||
if (updated) {
|
if (updated) {
|
||||||
saveMenuCache();
|
saveMenuCache();
|
||||||
updateLastUpdatedTime(new Date().toISOString());
|
|
||||||
localStorage.setItem('kantine_flagged_items_last_checked', new Date().toISOString());
|
localStorage.setItem('kantine_flagged_items_last_checked', new Date().toISOString());
|
||||||
updateAlarmBell();
|
updateAlarmBell();
|
||||||
renderVisibleWeeks();
|
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 { 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 { renderVisibleWeeks, openVersionMenu } from './ui_helpers.js';
|
||||||
import { API_BASE, GUEST_TOKEN } from './constants.js';
|
import { API_BASE, GUEST_TOKEN } from './constants.js';
|
||||||
import { apiHeaders } from './api.js';
|
import { apiHeaders } from './api.js';
|
||||||
@@ -162,6 +162,13 @@ export function bindEvents() {
|
|||||||
loadMenuDataFromAPI();
|
loadMenuDataFromAPI();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const bellBtn = document.getElementById('alarm-bell');
|
||||||
|
if (bellBtn) {
|
||||||
|
bellBtn.addEventListener('click', () => {
|
||||||
|
refreshFlaggedItems();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
btnLoginOpen.addEventListener('click', () => {
|
btnLoginOpen.addEventListener('click', () => {
|
||||||
loginModal.classList.remove('hidden');
|
loginModal.classList.remove('hidden');
|
||||||
document.getElementById('login-error').classList.add('hidden');
|
document.getElementById('login-error').classList.add('hidden');
|
||||||
|
|||||||
@@ -352,7 +352,8 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Refresh button animation */
|
/* 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;
|
animation: rotate 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,9 @@ const testCode = `
|
|||||||
alarmBtn.classList.add('hidden');
|
alarmBtn.classList.add('hidden');
|
||||||
if (!document.getElementById('alarm-bell').className.includes('hidden')) throw new Error("Bell should be 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 ---");
|
console.log("--- Testing Highlights Modal ---");
|
||||||
// First, verify initial state
|
// First, verify initial state
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v1.6.12
|
v1.6.14
|
||||||
|
|||||||
Reference in New Issue
Block a user