feat(ui): display matched highlight tags in menu cards (v1.2.4)

This commit is contained in:
2026-02-16 22:19:41 +01:00
parent f9b29254f9
commit 7296901ad9
8 changed files with 349 additions and 272 deletions

View File

@@ -1,3 +1,6 @@
## v1.2.4 (2026-02-16)
- **Feature**: Gefundene Highlights werden jetzt direkt im Menü als Badge angezeigt. 🏷️
## v1.2.3 (2026-02-16) ## v1.2.3 (2026-02-16)
- **Fix**: Update-Icon ist jetzt klickbar und führt direkt zum Installer. 🔗 - **Fix**: Update-Icon ist jetzt klickbar und führt direkt zum Installer. 🔗
- **Dev**: Unit-Tests für Update-Logik im Build integriert. 🛡️ - **Dev**: Unit-Tests für Update-Logik im Build integriert. 🛡️

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

16
dist/install.html vendored

File diff suppressed because one or more lines are too long

View File

@@ -1374,6 +1374,41 @@ body {
} }
/* Update Banner Enhanced */ /* Update Banner Enhanced */
.update-banner {
/* ... existing styles ... */
}
/* Matched Tags in Menu Card */
.matched-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 8px;
/* Space between tags and title */
margin-top: -5px;
/* Pull closer to header */
}
.tag-badge-small {
display: inline-flex;
align-items: center;
font-size: 0.7rem;
padding: 2px 8px;
border-radius: 4px;
background: rgba(59, 130, 246, 0.15);
color: #60a5fa;
border: 1px solid rgba(59, 130, 246, 0.3);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
[data-theme="light"] .tag-badge-small {
background: rgba(37, 99, 235, 0.1);
color: #2563eb;
border: 1px solid rgba(37, 99, 235, 0.2);
}
.change-summary { .change-summary {
font-size: 0.8rem; font-size: 0.8rem;
background: rgba(0, 0, 0, 0.1); background: rgba(0, 0, 0, 0.1);
@@ -1667,7 +1702,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 style="font-size: 0.6em; opacity: 0.7; font-weight: 400;">v1.2.3</small></h1> <h1>Kantinen Übersicht <small style="font-size: 0.6em; opacity: 0.7; font-weight: 400;">v1.2.4</small></h1>
<div id="last-updated-subtitle" class="subtitle"></div> <div id="last-updated-subtitle" class="subtitle"></div>
</div> </div>
</div> </div>
@@ -2306,9 +2341,9 @@ body {
} }
function checkHighlight(text) { function checkHighlight(text) {
if (!text) return false; if (!text) return [];
text = text.toLowerCase(); text = text.toLowerCase();
return highlightTags.some(tag => text.includes(tag)); return highlightTags.filter(tag => text.includes(tag));
} }
// === Local Menu Cache (localStorage) === // === Local Menu Cache (localStorage) ===
@@ -3005,7 +3040,7 @@ body {
// === Version Check === // === Version Check ===
async function checkForUpdates() { async function checkForUpdates() {
const CurrentVersion = 'v1.2.3'; const CurrentVersion = 'v1.2.4';
const VersionUrl = 'https://raw.githubusercontent.com/TauNeutrino/kantine-overview/main/version.txt'; const VersionUrl = 'https://raw.githubusercontent.com/TauNeutrino/kantine-overview/main/version.txt';
const InstallerUrl = 'https://htmlpreview.github.io/?https://github.com/TauNeutrino/kantine-overview/blob/main/dist/install.html'; const InstallerUrl = 'https://htmlpreview.github.io/?https://github.com/TauNeutrino/kantine-overview/blob/main/dist/install.html';
@@ -3067,13 +3102,13 @@ body {
lastUpdatedIcon.replaceWith(updateLink); lastUpdatedIcon.replaceWith(updateLink);
} }
} }
} catch (error) { } catch (error) {
console.warn('[Kantine] Version check failed:', error); console.warn('[Kantine] Version check failed:', error);
} }
} }
// === Order Countdown === // === Order Countdown ===
function updateCountdown() { function updateCountdown() {
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)
@@ -3152,69 +3187,69 @@ function updateCountdown() {
} else { } else {
countdownEl.classList.remove('urgent'); countdownEl.classList.remove('urgent');
} }
} }
function removeCountdown() { function removeCountdown() {
const el = document.getElementById('order-countdown'); const el = document.getElementById('order-countdown');
if (el) el.remove(); if (el) el.remove();
} }
// Update countdown every minute // Update countdown every minute
setInterval(updateCountdown, 60000); setInterval(updateCountdown, 60000);
// Also update on load // Also update on load
setTimeout(updateCountdown, 1000); setTimeout(updateCountdown, 1000);
// === Helpers === // === Helpers ===
function getISOWeek(date) { function getISOWeek(date) {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7; const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum); d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil(((d - yearStart) / 86400000 + 1) / 7); return Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
} }
function getWeekYear(d) { function getWeekYear(d) {
const date = new Date(d.getTime()); const date = new Date(d.getTime());
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
return date.getFullYear(); return date.getFullYear();
} }
function translateDay(englishDay) { function translateDay(englishDay) {
const map = { Monday: 'Montag', Tuesday: 'Dienstag', Wednesday: 'Mittwoch', Thursday: 'Donnerstag', Friday: 'Freitag', Saturday: 'Samstag', Sunday: 'Sonntag' }; const map = { Monday: 'Montag', Tuesday: 'Dienstag', Wednesday: 'Mittwoch', Thursday: 'Donnerstag', Friday: 'Freitag', Saturday: 'Samstag', Sunday: 'Sonntag' };
return map[englishDay] || englishDay; return map[englishDay] || englishDay;
} }
function escapeHtml(text) { function escapeHtml(text) {
const div = document.createElement('div'); const div = document.createElement('div');
div.textContent = text || ''; div.textContent = text || '';
return div.innerHTML; return div.innerHTML;
} }
// === Bootstrap === // === Bootstrap ===
injectUI(); injectUI();
bindEvents(); bindEvents();
updateAuthUI(); updateAuthUI();
cleanupExpiredFlags(); cleanupExpiredFlags();
// Load cached data first for instant UI, then refresh from API // Load cached data first for instant UI, then refresh from API
const hadCache = loadMenuCache(); const hadCache = loadMenuCache();
if (hadCache) { if (hadCache) {
// Hide loading spinner since cache is shown // Hide loading spinner since cache is shown
document.getElementById('loading').classList.add('hidden'); document.getElementById('loading').classList.add('hidden');
} }
loadMenuDataFromAPI(); loadMenuDataFromAPI();
// Auto-start polling if already logged in // Auto-start polling if already logged in
if (authToken) { if (authToken) {
startPolling(); startPolling();
} }
// Check for updates // Check for updates
checkForUpdates(); checkForUpdates();
console.log('Kantine Wrapper loaded ✅'); console.log('Kantine Wrapper loaded ✅');
}) (); })();
// === Error Modal === // === Error Modal ===
function showErrorModal(title, htmlContent, btnText, url) { function showErrorModal(title, htmlContent, btnText, url) {

View File

@@ -705,9 +705,9 @@
} }
function checkHighlight(text) { function checkHighlight(text) {
if (!text) return false; if (!text) return [];
text = text.toLowerCase(); text = text.toLowerCase();
return highlightTags.some(tag => text.includes(tag)); return highlightTags.filter(tag => text.includes(tag));
} }
// === Local Menu Cache (localStorage) === // === Local Menu Cache (localStorage) ===
@@ -1466,13 +1466,13 @@
lastUpdatedIcon.replaceWith(updateLink); lastUpdatedIcon.replaceWith(updateLink);
} }
} }
} catch (error) { } catch (error) {
console.warn('[Kantine] Version check failed:', error); console.warn('[Kantine] Version check failed:', error);
} }
} }
// === Order Countdown === // === Order Countdown ===
function updateCountdown() { function updateCountdown() {
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)
@@ -1551,69 +1551,69 @@ function updateCountdown() {
} else { } else {
countdownEl.classList.remove('urgent'); countdownEl.classList.remove('urgent');
} }
} }
function removeCountdown() { function removeCountdown() {
const el = document.getElementById('order-countdown'); const el = document.getElementById('order-countdown');
if (el) el.remove(); if (el) el.remove();
} }
// Update countdown every minute // Update countdown every minute
setInterval(updateCountdown, 60000); setInterval(updateCountdown, 60000);
// Also update on load // Also update on load
setTimeout(updateCountdown, 1000); setTimeout(updateCountdown, 1000);
// === Helpers === // === Helpers ===
function getISOWeek(date) { function getISOWeek(date) {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7; const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum); d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil(((d - yearStart) / 86400000 + 1) / 7); return Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
} }
function getWeekYear(d) { function getWeekYear(d) {
const date = new Date(d.getTime()); const date = new Date(d.getTime());
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
return date.getFullYear(); return date.getFullYear();
} }
function translateDay(englishDay) { function translateDay(englishDay) {
const map = { Monday: 'Montag', Tuesday: 'Dienstag', Wednesday: 'Mittwoch', Thursday: 'Donnerstag', Friday: 'Freitag', Saturday: 'Samstag', Sunday: 'Sonntag' }; const map = { Monday: 'Montag', Tuesday: 'Dienstag', Wednesday: 'Mittwoch', Thursday: 'Donnerstag', Friday: 'Freitag', Saturday: 'Samstag', Sunday: 'Sonntag' };
return map[englishDay] || englishDay; return map[englishDay] || englishDay;
} }
function escapeHtml(text) { function escapeHtml(text) {
const div = document.createElement('div'); const div = document.createElement('div');
div.textContent = text || ''; div.textContent = text || '';
return div.innerHTML; return div.innerHTML;
} }
// === Bootstrap === // === Bootstrap ===
injectUI(); injectUI();
bindEvents(); bindEvents();
updateAuthUI(); updateAuthUI();
cleanupExpiredFlags(); cleanupExpiredFlags();
// Load cached data first for instant UI, then refresh from API // Load cached data first for instant UI, then refresh from API
const hadCache = loadMenuCache(); const hadCache = loadMenuCache();
if (hadCache) { if (hadCache) {
// Hide loading spinner since cache is shown // Hide loading spinner since cache is shown
document.getElementById('loading').classList.add('hidden'); document.getElementById('loading').classList.add('hidden');
} }
loadMenuDataFromAPI(); loadMenuDataFromAPI();
// Auto-start polling if already logged in // Auto-start polling if already logged in
if (authToken) { if (authToken) {
startPolling(); startPolling();
} }
// Check for updates // Check for updates
checkForUpdates(); checkForUpdates();
console.log('Kantine Wrapper loaded ✅'); console.log('Kantine Wrapper loaded ✅');
}) (); })();
// === Error Modal === // === Error Modal ===
function showErrorModal(title, htmlContent, btnText, url) { function showErrorModal(title, htmlContent, btnText, url) {

View File

@@ -1363,6 +1363,41 @@ body {
} }
/* Update Banner Enhanced */ /* Update Banner Enhanced */
.update-banner {
/* ... existing styles ... */
}
/* Matched Tags in Menu Card */
.matched-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 8px;
/* Space between tags and title */
margin-top: -5px;
/* Pull closer to header */
}
.tag-badge-small {
display: inline-flex;
align-items: center;
font-size: 0.7rem;
padding: 2px 8px;
border-radius: 4px;
background: rgba(59, 130, 246, 0.15);
color: #60a5fa;
border: 1px solid rgba(59, 130, 246, 0.3);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
[data-theme="light"] .tag-badge-small {
background: rgba(37, 99, 235, 0.1);
color: #2563eb;
border: 1px solid rgba(37, 99, 235, 0.2);
}
.change-summary { .change-summary {
font-size: 0.8rem; font-size: 0.8rem;
background: rgba(0, 0, 0, 0.1); background: rgba(0, 0, 0, 0.1);

View File

@@ -1 +1 @@
v1.2.3 v1.2.4