Compare commits

...

8 Commits

Author SHA1 Message Date
Kantine Wrapper
d4a8a47ccd chore: update build artifacts for v1.4.11 2026-02-24 13:11:09 +01:00
Kantine Wrapper
8e8c93410b feat: background refresh for version menu 2026-02-24 13:11:02 +01:00
Kantine Wrapper
cca59bcace chore: update build artifacts for v1.4.10 2026-02-24 13:05:53 +01:00
Kantine Wrapper
432bbcb6f2 build: separate release logic into release.sh 2026-02-24 13:05:46 +01:00
Kantine Wrapper
accdccf897 docs: sync REQUIREMENTS.md with latest features 2026-02-24 13:00:16 +01:00
Kantine Wrapper
7fc8c6f1e0 dist files for v1.4.10 built 2026-02-24 12:59:19 +01:00
Kantine Wrapper
0eb14a1869 dist files for v1.4.9 built 2026-02-24 12:56:53 +01:00
Kantine Wrapper
c841954c5d dist files for v1.4.8 built 2026-02-24 12:44:07 +01:00
10 changed files with 172 additions and 67 deletions

View File

@@ -40,7 +40,7 @@ Das System umfasst die Darstellung von Menüplänen in einer Wochenübersicht, d
| FR-033 | Es muss möglich sein, dasselbe Menü mehrfach zu bestellen. Bei Mehrfachbestellungen muss die Anzahl angezeigt werden. | Niedrig | v1.0.1 |
| **Kostentransparenz & Bestellhistorie** | | | |
| FR-040 | Das System muss die Gesamtkosten aller Bestellungen einer Woche automatisch berechnen und anzeigen. | Mittel | v1.1.0 |
| FR-041 | Das System muss dem Benutzer eine paginierte oder vollständige Bestellhistorie (gruppiert nach Monat und KW) mit Fortschrittsanzeige auf Abruf in einem Modal bereitstellen. | Mittel | v1.4.0 |
| FR-041 | Das System muss dem Benutzer eine Bestellhistorie (gruppiert nach Monat und KW) mit Fortschrittsanzeige auf Abruf in einem Modal bereitstellen. Die Historie muss über ein lokales Delta-Caching verfügen, um Ladezeiten zu minimieren. | Mittel | v1.4.0 (Update v1.4.7) |
| **Bestell-Countdown** | | | |
| FR-050 | Das System muss vor Bestellschluss einen visuell hervorgehobenen Countdown anzeigen. | Mittel | v1.1.0 |
| **Menü-Flagging & Benachrichtigungen** | | | |
@@ -59,7 +59,7 @@ Das System umfasst die Darstellung von Menüplänen in einer Wochenübersicht, d
| FR-082 | Das System muss beim erstmaligen Laden die Betriebssystem-Präferenz für das Farbschema berücksichtigen. | Niedrig | v1.0.1 |
| **Header UI & Navigation** | | | |
| 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, Grün=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.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.5.0 (Update v1.4.10) |
| FR-092 | Sobald über den Daten-Refresh erstmals Menüdaten für die Nächste Woche geladen werden, muss der entsprechende Navigation-Button animiert und farblich (Gelb) hervorgehoben werden. Zusätzlich muss einmalig ein Hinweis eingeblendet werden. Bei Klick auf den Button muss die Hervorhebung erlöschen. | Mittel | v1.6.0 |
| **Benutzer-Feedback** | | | |
| FR-090 | Alle benutzerrelevanten Aktionen (Bestellung, Stornierung, Fehler) müssen durch nicht-blockierende Benachrichtigungen (Toasts) bestätigt werden. | Mittel | v1.0.1 |
@@ -93,4 +93,4 @@ Das System umfasst die Darstellung von Menüplänen in einer Wochenübersicht, d
* **Datenhaltung**: Clientseitig via `localStorage` (Menü-Cache, Flags, Highlights, Theme) und `sessionStorage` (Auth-Token).
* **Build**: Bash-basiertes Build-Script, das Bookmarklet-URL, Standalone-HTML und Installer-Seite generiert.
* **Versionierung**: SemVer, verwaltet über GitHub Releases/Tags.
* **Tests**: Python-basierte Build-Tests + Node.js-basierte Logik-Tests.
* **Tests**: Python-basierte Build-Tests (`python3`) + Node.js-basierte Logik-Tests.

View File

@@ -267,23 +267,4 @@ if [ $TEST_EXIT -ne 0 ]; then
fi
echo "✅ All build tests passed."
# === 5. Commit, tag, and push ===
echo ""
echo "=== Committing & Pushing ==="
git add -A
git commit -m "dist files for $VERSION built" --allow-empty
echo ""
echo "=== Tagging $VERSION ==="
if git rev-parse "$VERSION" >/dev/null 2>&1; then
git tag -f "$VERSION"
echo "🔄 Tag $VERSION moved to current commit."
else
git tag "$VERSION"
echo "✅ Created tag: $VERSION"
fi
git push
git push origin --force tag "$VERSION"
git push github --force tag "$VERSION"
echo "✅ Pushed commit + tag $VERSION"

View File

@@ -1,3 +1,16 @@
## v1.4.11 (2026-02-24)
- **Feature**: Das Versionsmenü prüft nun im Hintergrund direkt beim Öffnen nach neuen Versionen und aktualisiert die Liste automatisch, selbst wenn eine veraltete Liste noch im Cache liegt.
## v1.4.10 (2026-02-24)
- **Fix**: Die Farben der Benachrichtigungs-Glocke wurden korrigiert: Sie ist nun gelb, während man auf ein Menü wartet, und wird grün, sobald eines verfügbar ist.
## v1.4.9 (2026-02-24)
- **Fix**: Das Glocken-Icon für Benachrichtigungen wird nun direkt beim Start (wenn Daten aus dem lokalen Cache geladen werden) korrekt angezeigt.
## v1.4.8 (2026-02-24)
- **Fix**: Die Benachrichtigungs-Glocke wird nun korrekt in Gelb dargestellt, wenn beobachtete Menüs verfügbar sind.
- **Tools**: Fehler in Testskript behoben, der den CI/CD Build verlangsamt hat.
## v1.4.7 (2026-02-24)
- **Performance**: Die Bestellhistorie nutzt nun einen inkrementellen Delta-Cache anstatt immer alle Seiten von der API herunterzuladen, was die Ladezeiten für Vielbesteller enorm reduziert.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

29
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.7</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.11</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.7</span>
<strong>Aktuell:</strong> <span id="version-current">v1.4.11</span>
</div>
<div class="dev-toggle">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
@@ -2930,11 +2930,11 @@ body {
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
if (anyAvailable) {
bellIcon.style.color = 'var(--success-color)';
bellIcon.style.textShadow = '0 0 10px rgba(16, 185, 129, 0.4)';
} else {
bellIcon.style.color = 'var(--warning-color)';
bellIcon.style.textShadow = '0 0 10px rgba(245, 158, 11, 0.4)';
} else {
bellIcon.style.color = 'var(--text-secondary)';
bellIcon.style.textShadow = 'none';
}
}
@@ -3108,6 +3108,7 @@ body {
console.log(`[Cache] Parsed ${allWeeks.length} weeks:`, allWeeks.map(w => `KW${w.weekNumber}/${w.year} (${(w.days || []).length} days)`));
renderVisibleWeeks();
updateNextWeekBadge();
updateAlarmBell();
if (cachedTs) updateLastUpdatedTime(cachedTs);
console.log('Loaded menu from cache');
return true;
@@ -3892,7 +3893,7 @@ body {
// Periodic update check (runs on init + every hour)
async function checkForUpdates() {
const currentVersion = 'v1.4.7';
const currentVersion = 'v1.4.11';
const devMode = localStorage.getItem('kantine_dev_mode') === 'true';
try {
@@ -3933,7 +3934,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.7';
const currentVersion = 'v1.4.11';
if (!modal) return;
modal.classList.remove('hidden');
@@ -3951,19 +3952,8 @@ body {
const dm = devToggle.checked;
container.innerHTML = '<p style="color:var(--text-secondary);">Lade Versionen...</p>';
try {
let versions;
const cached = JSON.parse(localStorage.getItem('kantine_version_cache') || 'null');
if (!forceRefresh && cached && cached.devMode === dm && (Date.now() - cached.timestamp < 3600000)) {
versions = cached.versions;
} else {
versions = await fetchVersions(dm);
localStorage.setItem('kantine_version_cache', JSON.stringify({
timestamp: Date.now(), devMode: dm, versions
}));
}
if (!versions.length) {
function renderVersionsList(versions) {
if (!versions || !versions.length) {
container.innerHTML = '<p style="color:var(--text-secondary);">Keine Versionen gefunden.</p>';
return;
}
@@ -3995,6 +3985,34 @@ body {
`;
list.appendChild(li);
});
}
try {
// 1. Show cached versions immediately if available
const cachedRaw = localStorage.getItem('kantine_version_cache');
let cached = null;
if (cachedRaw) {
try { cached = JSON.parse(cachedRaw); } catch (e) { }
}
if (cached && cached.devMode === dm && cached.versions) {
renderVersionsList(cached.versions);
}
// 2. Fetch fresh versions in background (or foreground if no cache)
const liveVersions = await fetchVersions(dm);
// Compare with cache to see if we need to re-render
const liveVersionsStr = JSON.stringify(liveVersions);
const cachedVersionsStr = cached ? JSON.stringify(cached.versions) : '';
if (liveVersionsStr !== cachedVersionsStr) {
localStorage.setItem('kantine_version_cache', JSON.stringify({
timestamp: Date.now(), devMode: dm, versions: liveVersions
}));
renderVersionsList(liveVersions);
}
} catch (e) {
container.innerHTML = `<p style="color:#e94560;">Fehler: ${e.message}</p>`;
}

View File

@@ -980,11 +980,11 @@
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
if (anyAvailable) {
bellIcon.style.color = 'var(--success-color)';
bellIcon.style.textShadow = '0 0 10px rgba(16, 185, 129, 0.4)';
} else {
bellIcon.style.color = 'var(--warning-color)';
bellIcon.style.textShadow = '0 0 10px rgba(245, 158, 11, 0.4)';
} else {
bellIcon.style.color = 'var(--text-secondary)';
bellIcon.style.textShadow = 'none';
}
}
@@ -1158,6 +1158,7 @@
console.log(`[Cache] Parsed ${allWeeks.length} weeks:`, allWeeks.map(w => `KW${w.weekNumber}/${w.year} (${(w.days || []).length} days)`));
renderVisibleWeeks();
updateNextWeekBadge();
updateAlarmBell();
if (cachedTs) updateLastUpdatedTime(cachedTs);
console.log('Loaded menu from cache');
return true;
@@ -2001,19 +2002,8 @@
const dm = devToggle.checked;
container.innerHTML = '<p style="color:var(--text-secondary);">Lade Versionen...</p>';
try {
let versions;
const cached = JSON.parse(localStorage.getItem('kantine_version_cache') || 'null');
if (!forceRefresh && cached && cached.devMode === dm && (Date.now() - cached.timestamp < 3600000)) {
versions = cached.versions;
} else {
versions = await fetchVersions(dm);
localStorage.setItem('kantine_version_cache', JSON.stringify({
timestamp: Date.now(), devMode: dm, versions
}));
}
if (!versions.length) {
function renderVersionsList(versions) {
if (!versions || !versions.length) {
container.innerHTML = '<p style="color:var(--text-secondary);">Keine Versionen gefunden.</p>';
return;
}
@@ -2045,6 +2035,34 @@
`;
list.appendChild(li);
});
}
try {
// 1. Show cached versions immediately if available
const cachedRaw = localStorage.getItem('kantine_version_cache');
let cached = null;
if (cachedRaw) {
try { cached = JSON.parse(cachedRaw); } catch (e) { }
}
if (cached && cached.devMode === dm && cached.versions) {
renderVersionsList(cached.versions);
}
// 2. Fetch fresh versions in background (or foreground if no cache)
const liveVersions = await fetchVersions(dm);
// Compare with cache to see if we need to re-render
const liveVersionsStr = JSON.stringify(liveVersions);
const cachedVersionsStr = cached ? JSON.stringify(cached.versions) : '';
if (liveVersionsStr !== cachedVersionsStr) {
localStorage.setItem('kantine_version_cache', JSON.stringify({
timestamp: Date.now(), devMode: dm, versions: liveVersions
}));
renderVersionsList(liveVersions);
}
} catch (e) {
container.innerHTML = `<p style="color:#e94560;">Fehler: ${e.message}</p>`;
}

58
release.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
# release.sh - Deploys a new version of the Kantine Wrapper
# Ensure we're in the script directory
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
cd "$SCRIPT_DIR"
# Ensure tests have run and artifacts exist
if [ ! -d "$SCRIPT_DIR/dist" ]; then
echo "❌ Error: dist folder missing. Please run build-bookmarklet.sh first"
exit 1
fi
# Get current version
VERSION=$(cat "version.txt" | tr -d '\n\r ')
# Validate that version is set
if [ -z "$VERSION" ]; then
echo "❌ Error: Could not determine version from version.txt"
exit 1
fi
echo "=== Kantine Bookmarklet Releaser ($VERSION) ==="
# Check for uncommitted changes (excluding dist/)
if ! git diff-index --quiet HEAD -- ":(exclude)dist"; then
echo "⚠️ Warning: You have uncommitted changes in the working directory."
echo "Please commit your code changes before running the release script."
exit 1
fi
echo "=== Committing build artifacts ==="
git add "dist/"
git commit -m "chore: update build artifacts for $VERSION" --allow-empty
echo ""
echo "=== Tagging $VERSION ==="
if git rev-parse "$VERSION" >/dev/null 2>&1; then
git tag -f "$VERSION"
echo "🔄 Tag $VERSION moved to current commit."
else
git tag "$VERSION"
echo "✅ Created tag: $VERSION"
fi
echo ""
echo "=== Pushing to remotes ==="
# Determine remote targets: Assume 'origin' for primary, optionally 'github'
git push origin HEAD
git push origin --force tag "$VERSION"
# If a remote named 'github' exists, push tags there too
if git remote | grep -q "^github$"; then
git push github --force tag "$VERSION"
fi
echo "🎉 Successfully released version $VERSION!"
exit 0

View File

@@ -1 +1 @@
v1.4.7
v1.4.11