Compare commits
15 Commits
v1.4.5
...
fd765a74c0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd765a74c0 | ||
|
|
1f184fab8b | ||
|
|
b6c7c66027 | ||
|
|
33bb87d7f4 | ||
|
|
d4a8a47ccd | ||
|
|
8e8c93410b | ||
|
|
cca59bcace | ||
|
|
432bbcb6f2 | ||
|
|
accdccf897 | ||
|
|
7fc8c6f1e0 | ||
|
|
0eb14a1869 | ||
|
|
c841954c5d | ||
|
|
320c4066f3 | ||
|
|
cda74e65db | ||
|
|
d1a19b043d |
@@ -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 |
|
| 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** | | | |
|
| **Kostentransparenz & Bestellhistorie** | | | |
|
||||||
| FR-040 | Das System muss die Gesamtkosten aller Bestellungen einer Woche automatisch berechnen und anzeigen. | Mittel | v1.1.0 |
|
| 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** | | | |
|
| **Bestell-Countdown** | | | |
|
||||||
| FR-050 | Das System muss vor Bestellschluss einen visuell hervorgehobenen Countdown anzeigen. | Mittel | v1.1.0 |
|
| FR-050 | Das System muss vor Bestellschluss einen visuell hervorgehobenen Countdown anzeigen. | Mittel | v1.1.0 |
|
||||||
| **Menü-Flagging & Benachrichtigungen** | | | |
|
| **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 |
|
| 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** | | | |
|
| **Header UI & Navigation** | | | |
|
||||||
| 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, 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 |
|
| 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** | | | |
|
| **Benutzer-Feedback** | | | |
|
||||||
| FR-090 | Alle benutzerrelevanten Aktionen (Bestellung, Stornierung, Fehler) müssen durch nicht-blockierende Benachrichtigungen (Toasts) bestätigt werden. | Mittel | v1.0.1 |
|
| 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).
|
* **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.
|
* **Build**: Bash-basiertes Build-Script, das Bookmarklet-URL, Standalone-HTML und Installer-Seite generiert.
|
||||||
* **Versionierung**: SemVer, verwaltet über GitHub Releases/Tags.
|
* **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.
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ cat > "$DIST_DIR/install.html" << INSTALLEOF
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="text-align: center; margin-top: 40px; color: #5c6b7f; font-size: 0.8rem;">
|
<div style="text-align: center; margin-top: 40px; color: #5c6b7f; font-size: 0.8rem;">
|
||||||
<p>Powered by <strong>Kaufi-Kitchen</strong> 👨🍳</p>
|
<p>Powered by <strong>Kaufis-Kitchen</strong> 👨🍳</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -267,23 +267,4 @@ if [ $TEST_EXIT -ne 0 ]; then
|
|||||||
fi
|
fi
|
||||||
echo "✅ All build tests passed."
|
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"
|
|
||||||
|
|||||||
25
changelog.md
25
changelog.md
@@ -1,3 +1,28 @@
|
|||||||
|
## v1.4.13 (2026-02-24)
|
||||||
|
- **Fix**: Die Farben der Glocke funktionieren nun verlässlich, da CSS-Variablen durch direkte Hex-Codes ersetzt wurden.
|
||||||
|
|
||||||
|
## v1.4.12 (2026-02-24)
|
||||||
|
- **Fix**: Das Glocken-Icon sollte nun endgültig versteckt bleiben, wenn keine Benachrichtigungen aktiv sind (CSS-Kollision mit `.hidden` behoben).
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## v1.4.6 (2026-02-24)
|
||||||
|
- **Fix**: Die Umrandung für bereits bestellte Menüs der vergangenen Tage ist nun ebenfalls einheitlich violett statt blau.
|
||||||
|
|
||||||
## v1.4.5 (2026-02-24)
|
## v1.4.5 (2026-02-24)
|
||||||
- **Fix**: Doppelten Scrollbalken in der Versionen-Liste entfernt.
|
- **Fix**: Doppelten Scrollbalken in der Versionen-Liste entfernt.
|
||||||
|
|
||||||
|
|||||||
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
47
dist/install.html
vendored
47
dist/install.html
vendored
File diff suppressed because one or more lines are too long
172
dist/kantine-standalone.html
vendored
172
dist/kantine-standalone.html
vendored
@@ -812,7 +812,7 @@ body {
|
|||||||
/* No opacity/filter here - fully visible */
|
/* No opacity/filter here - fully visible */
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
border: 1px solid var(--accent-color);
|
border: 1px solid #8b5cf6;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: 0 -1rem 1.5rem -1rem;
|
margin: 0 -1rem 1.5rem -1rem;
|
||||||
@@ -2021,7 +2021,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 class="version-tag" style="font-size: 0.6em; opacity: 0.7; font-weight: 400; cursor: pointer;" title="Klick für Versionsmenü">v1.4.5</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.13</small></h1>
|
||||||
<div id="last-updated-subtitle" class="subtitle"></div>
|
<div id="last-updated-subtitle" class="subtitle"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-group" style="margin-left: 1rem;">
|
<div class="nav-group" style="margin-left: 1rem;">
|
||||||
@@ -2163,7 +2163,7 @@ body {
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div style="margin-bottom: 1rem;">
|
<div style="margin-bottom: 1rem;">
|
||||||
<strong>Aktuell:</strong> <span id="version-current">v1.4.5</span>
|
<strong>Aktuell:</strong> <span id="version-current">v1.4.13</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dev-toggle">
|
<div class="dev-toggle">
|
||||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
|
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
|
||||||
@@ -2520,37 +2520,49 @@ body {
|
|||||||
const progressFill = document.getElementById('history-progress-fill');
|
const progressFill = document.getElementById('history-progress-fill');
|
||||||
const progressText = document.getElementById('history-progress-text');
|
const progressText = document.getElementById('history-progress-text');
|
||||||
|
|
||||||
// Check memory cache first
|
// Check local storage cache (we still use memory cache if available)
|
||||||
|
let localCache = [];
|
||||||
if (fullOrderHistoryCache) {
|
if (fullOrderHistoryCache) {
|
||||||
renderHistory(fullOrderHistoryCache);
|
localCache = fullOrderHistoryCache;
|
||||||
return;
|
} else {
|
||||||
}
|
const ls = localStorage.getItem('kantine_history_cache');
|
||||||
|
if (ls) {
|
||||||
// Check local storage cache
|
|
||||||
const localCache = localStorage.getItem('kantine_history_cache');
|
|
||||||
if (localCache) {
|
|
||||||
try {
|
try {
|
||||||
fullOrderHistoryCache = JSON.parse(localCache);
|
localCache = JSON.parse(ls);
|
||||||
renderHistory(fullOrderHistoryCache);
|
fullOrderHistoryCache = localCache;
|
||||||
return;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('History cache parse error', e);
|
console.warn('History cache parse error', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show cached version immediately if we have one
|
||||||
|
if (localCache.length > 0) {
|
||||||
|
renderHistory(localCache);
|
||||||
|
}
|
||||||
|
|
||||||
if (!authToken) return;
|
if (!authToken) return;
|
||||||
|
|
||||||
|
// Start background delta sync
|
||||||
|
if (localCache.length === 0) {
|
||||||
historyContent.innerHTML = '';
|
historyContent.innerHTML = '';
|
||||||
historyLoading.classList.remove('hidden');
|
historyLoading.classList.remove('hidden');
|
||||||
progressFill.style.width = '0%';
|
}
|
||||||
progressText.textContent = 'Lade Bestellhistorie...';
|
|
||||||
|
|
||||||
let nextUrl = `${API_BASE}/user/orders/?venue=${VENUE_ID}&ordering=-created&limit=50`;
|
progressFill.style.width = '0%';
|
||||||
let allOrders = [];
|
progressText.textContent = localCache.length > 0 ? 'Suche nach neuen Bestellungen...' : 'Lade Bestellhistorie...';
|
||||||
|
if (localCache.length > 0) historyLoading.classList.remove('hidden');
|
||||||
|
|
||||||
|
let nextUrl = localCache.length > 0
|
||||||
|
? `${API_BASE}/user/orders/?venue=${VENUE_ID}&ordering=-created&limit=5`
|
||||||
|
: `${API_BASE}/user/orders/?venue=${VENUE_ID}&ordering=-created&limit=50`;
|
||||||
|
let fetchedOrders = [];
|
||||||
let totalCount = 0;
|
let totalCount = 0;
|
||||||
|
let requiresFullFetch = localCache.length === 0;
|
||||||
|
let deltaComplete = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (nextUrl) {
|
while (nextUrl && !deltaComplete) {
|
||||||
const response = await fetch(nextUrl, { headers: apiHeaders(authToken) });
|
const response = await fetch(nextUrl, { headers: apiHeaders(authToken) });
|
||||||
if (!response.ok) throw new Error(`Fetch failed: ${response.status}`);
|
if (!response.ok) throw new Error(`Fetch failed: ${response.status}`);
|
||||||
|
|
||||||
@@ -2560,29 +2572,75 @@ body {
|
|||||||
totalCount = data.count;
|
totalCount = data.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
allOrders = allOrders.concat(data.results || []);
|
const results = data.results || [];
|
||||||
|
|
||||||
|
for (const order of results) {
|
||||||
|
// Check if we hit an order that is already in our cache AND has the exact same state/update time
|
||||||
|
// Bessa returns 'updated' timestamp, we can use it to determine if anything changed
|
||||||
|
const existingOrderIndex = localCache.findIndex(cached => cached.id === order.id);
|
||||||
|
|
||||||
|
if (!requiresFullFetch && existingOrderIndex !== -1) {
|
||||||
|
const existingOrder = localCache[existingOrderIndex];
|
||||||
|
// If order exists and wasn't updated since our cache, we've reached the point
|
||||||
|
// where everything older is already correctly cached.
|
||||||
|
// order.updated is an ISO string like "2025-02-18T10:30:15.123456Z"
|
||||||
|
if (existingOrder.updated === order.updated && existingOrder.order_state === order.order_state) {
|
||||||
|
deltaComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchedOrders.push(order);
|
||||||
|
}
|
||||||
|
|
||||||
// Update progress
|
// Update progress
|
||||||
|
if (!deltaComplete && requiresFullFetch) {
|
||||||
if (totalCount > 0) {
|
if (totalCount > 0) {
|
||||||
const pct = Math.round((allOrders.length / totalCount) * 100);
|
const pct = Math.round((fetchedOrders.length / totalCount) * 100);
|
||||||
progressFill.style.width = `${pct}%`;
|
progressFill.style.width = `${pct}%`;
|
||||||
progressText.textContent = `Lade Bestellung ${allOrders.length} von ${totalCount}...`;
|
progressText.textContent = `Lade Bestellung ${fetchedOrders.length} von ${totalCount}...`;
|
||||||
} else {
|
} else {
|
||||||
progressText.textContent = `Lade Bestellung ${allOrders.length}...`;
|
progressText.textContent = `Lade Bestellung ${fetchedOrders.length}...`;
|
||||||
|
}
|
||||||
|
} else if (!deltaComplete) {
|
||||||
|
progressText.textContent = `${fetchedOrders.length} neue/geänderte Bestellungen gefunden...`;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextUrl = data.next;
|
nextUrl = deltaComplete ? null : data.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
fullOrderHistoryCache = allOrders;
|
// Merge fetched orders with cache
|
||||||
|
if (fetchedOrders.length > 0) {
|
||||||
|
// We have new/updated orders. We need to merge them into the cache.
|
||||||
|
// 1. Create a map of the existing cache for quick ID lookup
|
||||||
|
const cacheMap = new Map(localCache.map(o => [o.id, o]));
|
||||||
|
|
||||||
|
// 2. Update/Insert the newly fetched orders
|
||||||
|
for (const order of fetchedOrders) {
|
||||||
|
cacheMap.set(order.id, order); // Overwrites existing, or adds new
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Convert back to array and sort by created date (descending)
|
||||||
|
const mergedOrders = Array.from(cacheMap.values());
|
||||||
|
mergedOrders.sort((a, b) => new Date(b.created) - new Date(a.created));
|
||||||
|
|
||||||
|
fullOrderHistoryCache = mergedOrders;
|
||||||
try {
|
try {
|
||||||
localStorage.setItem('kantine_history_cache', JSON.stringify(allOrders));
|
localStorage.setItem('kantine_history_cache', JSON.stringify(mergedOrders));
|
||||||
} catch (e) { console.warn('History cache write error', e); }
|
} catch (e) {
|
||||||
|
console.warn('History cache write error', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the updated history
|
||||||
renderHistory(fullOrderHistoryCache);
|
renderHistory(fullOrderHistoryCache);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching full history:', error);
|
console.error('Error in history sync:', error);
|
||||||
|
if (localCache.length === 0) {
|
||||||
historyContent.innerHTML = `<p style="color:var(--error-color);text-align:center;">Fehler beim Laden der Historie.</p>`;
|
historyContent.innerHTML = `<p style="color:var(--error-color);text-align:center;">Fehler beim Laden der Historie.</p>`;
|
||||||
|
} else {
|
||||||
|
showToast('Hintergrund-Synchronisation fehlgeschlagen', 'error');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
historyLoading.classList.add('hidden');
|
historyLoading.classList.add('hidden');
|
||||||
}
|
}
|
||||||
@@ -2784,7 +2842,7 @@ body {
|
|||||||
|
|
||||||
if (response.ok || response.status === 201) {
|
if (response.ok || response.status === 201) {
|
||||||
showToast(`Bestellt: ${name}`, 'success');
|
showToast(`Bestellt: ${name}`, 'success');
|
||||||
localStorage.removeItem('kantine_history_cache');
|
fullOrderHistoryCache = null; // Clear memory cache so next history open triggers delta sync
|
||||||
await fetchOrders();
|
await fetchOrders();
|
||||||
} else {
|
} else {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -2814,7 +2872,7 @@ body {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
showToast(`Storniert: ${name}`, 'success');
|
showToast(`Storniert: ${name}`, 'success');
|
||||||
localStorage.removeItem('kantine_history_cache');
|
fullOrderHistoryCache = null; // Clear memory cache so next history open triggers delta sync
|
||||||
await fetchOrders();
|
await fetchOrders();
|
||||||
} else {
|
} else {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -2838,6 +2896,8 @@ body {
|
|||||||
|
|
||||||
if (userFlags.size === 0) {
|
if (userFlags.size === 0) {
|
||||||
bellBtn.classList.add('hidden');
|
bellBtn.classList.add('hidden');
|
||||||
|
bellIcon.style.color = 'var(--text-secondary)';
|
||||||
|
bellIcon.style.textShadow = 'none';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2872,10 +2932,10 @@ body {
|
|||||||
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
|
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
|
||||||
|
|
||||||
if (anyAvailable) {
|
if (anyAvailable) {
|
||||||
bellIcon.style.color = 'var(--success-color)';
|
bellIcon.style.color = '#10b981'; // green / success
|
||||||
bellIcon.style.textShadow = '0 0 10px rgba(16, 185, 129, 0.4)';
|
bellIcon.style.textShadow = '0 0 10px rgba(16, 185, 129, 0.4)';
|
||||||
} else {
|
} else {
|
||||||
bellIcon.style.color = 'var(--warning-color)';
|
bellIcon.style.color = '#f59e0b'; // yellow / warning
|
||||||
bellIcon.style.textShadow = '0 0 10px rgba(245, 158, 11, 0.4)';
|
bellIcon.style.textShadow = '0 0 10px rgba(245, 158, 11, 0.4)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3050,6 +3110,7 @@ body {
|
|||||||
console.log(`[Cache] Parsed ${allWeeks.length} weeks:`, allWeeks.map(w => `KW${w.weekNumber}/${w.year} (${(w.days || []).length} days)`));
|
console.log(`[Cache] Parsed ${allWeeks.length} weeks:`, allWeeks.map(w => `KW${w.weekNumber}/${w.year} (${(w.days || []).length} days)`));
|
||||||
renderVisibleWeeks();
|
renderVisibleWeeks();
|
||||||
updateNextWeekBadge();
|
updateNextWeekBadge();
|
||||||
|
updateAlarmBell();
|
||||||
if (cachedTs) updateLastUpdatedTime(cachedTs);
|
if (cachedTs) updateLastUpdatedTime(cachedTs);
|
||||||
console.log('Loaded menu from cache');
|
console.log('Loaded menu from cache');
|
||||||
return true;
|
return true;
|
||||||
@@ -3834,7 +3895,7 @@ body {
|
|||||||
|
|
||||||
// Periodic update check (runs on init + every hour)
|
// Periodic update check (runs on init + every hour)
|
||||||
async function checkForUpdates() {
|
async function checkForUpdates() {
|
||||||
const currentVersion = 'v1.4.5';
|
const currentVersion = 'v1.4.13';
|
||||||
const devMode = localStorage.getItem('kantine_dev_mode') === 'true';
|
const devMode = localStorage.getItem('kantine_dev_mode') === 'true';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -3875,7 +3936,7 @@ body {
|
|||||||
const modal = document.getElementById('version-modal');
|
const modal = document.getElementById('version-modal');
|
||||||
const container = document.getElementById('version-list-container');
|
const container = document.getElementById('version-list-container');
|
||||||
const devToggle = document.getElementById('dev-mode-toggle');
|
const devToggle = document.getElementById('dev-mode-toggle');
|
||||||
const currentVersion = 'v1.4.5';
|
const currentVersion = 'v1.4.13';
|
||||||
|
|
||||||
if (!modal) return;
|
if (!modal) return;
|
||||||
modal.classList.remove('hidden');
|
modal.classList.remove('hidden');
|
||||||
@@ -3893,19 +3954,8 @@ body {
|
|||||||
const dm = devToggle.checked;
|
const dm = devToggle.checked;
|
||||||
container.innerHTML = '<p style="color:var(--text-secondary);">Lade Versionen...</p>';
|
container.innerHTML = '<p style="color:var(--text-secondary);">Lade Versionen...</p>';
|
||||||
|
|
||||||
try {
|
function renderVersionsList(versions) {
|
||||||
let versions;
|
if (!versions || !versions.length) {
|
||||||
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) {
|
|
||||||
container.innerHTML = '<p style="color:var(--text-secondary);">Keine Versionen gefunden.</p>';
|
container.innerHTML = '<p style="color:var(--text-secondary);">Keine Versionen gefunden.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3937,6 +3987,34 @@ body {
|
|||||||
`;
|
`;
|
||||||
list.appendChild(li);
|
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) {
|
} catch (e) {
|
||||||
container.innerHTML = `<p style="color:#e94560;">Fehler: ${e.message}</p>`;
|
container.innerHTML = `<p style="color:#e94560;">Fehler: ${e.message}</p>`;
|
||||||
}
|
}
|
||||||
|
|||||||
162
kantine.js
162
kantine.js
@@ -570,37 +570,49 @@
|
|||||||
const progressFill = document.getElementById('history-progress-fill');
|
const progressFill = document.getElementById('history-progress-fill');
|
||||||
const progressText = document.getElementById('history-progress-text');
|
const progressText = document.getElementById('history-progress-text');
|
||||||
|
|
||||||
// Check memory cache first
|
// Check local storage cache (we still use memory cache if available)
|
||||||
|
let localCache = [];
|
||||||
if (fullOrderHistoryCache) {
|
if (fullOrderHistoryCache) {
|
||||||
renderHistory(fullOrderHistoryCache);
|
localCache = fullOrderHistoryCache;
|
||||||
return;
|
} else {
|
||||||
}
|
const ls = localStorage.getItem('kantine_history_cache');
|
||||||
|
if (ls) {
|
||||||
// Check local storage cache
|
|
||||||
const localCache = localStorage.getItem('kantine_history_cache');
|
|
||||||
if (localCache) {
|
|
||||||
try {
|
try {
|
||||||
fullOrderHistoryCache = JSON.parse(localCache);
|
localCache = JSON.parse(ls);
|
||||||
renderHistory(fullOrderHistoryCache);
|
fullOrderHistoryCache = localCache;
|
||||||
return;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('History cache parse error', e);
|
console.warn('History cache parse error', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show cached version immediately if we have one
|
||||||
|
if (localCache.length > 0) {
|
||||||
|
renderHistory(localCache);
|
||||||
|
}
|
||||||
|
|
||||||
if (!authToken) return;
|
if (!authToken) return;
|
||||||
|
|
||||||
|
// Start background delta sync
|
||||||
|
if (localCache.length === 0) {
|
||||||
historyContent.innerHTML = '';
|
historyContent.innerHTML = '';
|
||||||
historyLoading.classList.remove('hidden');
|
historyLoading.classList.remove('hidden');
|
||||||
progressFill.style.width = '0%';
|
}
|
||||||
progressText.textContent = 'Lade Bestellhistorie...';
|
|
||||||
|
|
||||||
let nextUrl = `${API_BASE}/user/orders/?venue=${VENUE_ID}&ordering=-created&limit=50`;
|
progressFill.style.width = '0%';
|
||||||
let allOrders = [];
|
progressText.textContent = localCache.length > 0 ? 'Suche nach neuen Bestellungen...' : 'Lade Bestellhistorie...';
|
||||||
|
if (localCache.length > 0) historyLoading.classList.remove('hidden');
|
||||||
|
|
||||||
|
let nextUrl = localCache.length > 0
|
||||||
|
? `${API_BASE}/user/orders/?venue=${VENUE_ID}&ordering=-created&limit=5`
|
||||||
|
: `${API_BASE}/user/orders/?venue=${VENUE_ID}&ordering=-created&limit=50`;
|
||||||
|
let fetchedOrders = [];
|
||||||
let totalCount = 0;
|
let totalCount = 0;
|
||||||
|
let requiresFullFetch = localCache.length === 0;
|
||||||
|
let deltaComplete = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (nextUrl) {
|
while (nextUrl && !deltaComplete) {
|
||||||
const response = await fetch(nextUrl, { headers: apiHeaders(authToken) });
|
const response = await fetch(nextUrl, { headers: apiHeaders(authToken) });
|
||||||
if (!response.ok) throw new Error(`Fetch failed: ${response.status}`);
|
if (!response.ok) throw new Error(`Fetch failed: ${response.status}`);
|
||||||
|
|
||||||
@@ -610,29 +622,75 @@
|
|||||||
totalCount = data.count;
|
totalCount = data.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
allOrders = allOrders.concat(data.results || []);
|
const results = data.results || [];
|
||||||
|
|
||||||
|
for (const order of results) {
|
||||||
|
// Check if we hit an order that is already in our cache AND has the exact same state/update time
|
||||||
|
// Bessa returns 'updated' timestamp, we can use it to determine if anything changed
|
||||||
|
const existingOrderIndex = localCache.findIndex(cached => cached.id === order.id);
|
||||||
|
|
||||||
|
if (!requiresFullFetch && existingOrderIndex !== -1) {
|
||||||
|
const existingOrder = localCache[existingOrderIndex];
|
||||||
|
// If order exists and wasn't updated since our cache, we've reached the point
|
||||||
|
// where everything older is already correctly cached.
|
||||||
|
// order.updated is an ISO string like "2025-02-18T10:30:15.123456Z"
|
||||||
|
if (existingOrder.updated === order.updated && existingOrder.order_state === order.order_state) {
|
||||||
|
deltaComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchedOrders.push(order);
|
||||||
|
}
|
||||||
|
|
||||||
// Update progress
|
// Update progress
|
||||||
|
if (!deltaComplete && requiresFullFetch) {
|
||||||
if (totalCount > 0) {
|
if (totalCount > 0) {
|
||||||
const pct = Math.round((allOrders.length / totalCount) * 100);
|
const pct = Math.round((fetchedOrders.length / totalCount) * 100);
|
||||||
progressFill.style.width = `${pct}%`;
|
progressFill.style.width = `${pct}%`;
|
||||||
progressText.textContent = `Lade Bestellung ${allOrders.length} von ${totalCount}...`;
|
progressText.textContent = `Lade Bestellung ${fetchedOrders.length} von ${totalCount}...`;
|
||||||
} else {
|
} else {
|
||||||
progressText.textContent = `Lade Bestellung ${allOrders.length}...`;
|
progressText.textContent = `Lade Bestellung ${fetchedOrders.length}...`;
|
||||||
|
}
|
||||||
|
} else if (!deltaComplete) {
|
||||||
|
progressText.textContent = `${fetchedOrders.length} neue/geänderte Bestellungen gefunden...`;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextUrl = data.next;
|
nextUrl = deltaComplete ? null : data.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
fullOrderHistoryCache = allOrders;
|
// Merge fetched orders with cache
|
||||||
|
if (fetchedOrders.length > 0) {
|
||||||
|
// We have new/updated orders. We need to merge them into the cache.
|
||||||
|
// 1. Create a map of the existing cache for quick ID lookup
|
||||||
|
const cacheMap = new Map(localCache.map(o => [o.id, o]));
|
||||||
|
|
||||||
|
// 2. Update/Insert the newly fetched orders
|
||||||
|
for (const order of fetchedOrders) {
|
||||||
|
cacheMap.set(order.id, order); // Overwrites existing, or adds new
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Convert back to array and sort by created date (descending)
|
||||||
|
const mergedOrders = Array.from(cacheMap.values());
|
||||||
|
mergedOrders.sort((a, b) => new Date(b.created) - new Date(a.created));
|
||||||
|
|
||||||
|
fullOrderHistoryCache = mergedOrders;
|
||||||
try {
|
try {
|
||||||
localStorage.setItem('kantine_history_cache', JSON.stringify(allOrders));
|
localStorage.setItem('kantine_history_cache', JSON.stringify(mergedOrders));
|
||||||
} catch (e) { console.warn('History cache write error', e); }
|
} catch (e) {
|
||||||
|
console.warn('History cache write error', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the updated history
|
||||||
renderHistory(fullOrderHistoryCache);
|
renderHistory(fullOrderHistoryCache);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching full history:', error);
|
console.error('Error in history sync:', error);
|
||||||
|
if (localCache.length === 0) {
|
||||||
historyContent.innerHTML = `<p style="color:var(--error-color);text-align:center;">Fehler beim Laden der Historie.</p>`;
|
historyContent.innerHTML = `<p style="color:var(--error-color);text-align:center;">Fehler beim Laden der Historie.</p>`;
|
||||||
|
} else {
|
||||||
|
showToast('Hintergrund-Synchronisation fehlgeschlagen', 'error');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
historyLoading.classList.add('hidden');
|
historyLoading.classList.add('hidden');
|
||||||
}
|
}
|
||||||
@@ -834,7 +892,7 @@
|
|||||||
|
|
||||||
if (response.ok || response.status === 201) {
|
if (response.ok || response.status === 201) {
|
||||||
showToast(`Bestellt: ${name}`, 'success');
|
showToast(`Bestellt: ${name}`, 'success');
|
||||||
localStorage.removeItem('kantine_history_cache');
|
fullOrderHistoryCache = null; // Clear memory cache so next history open triggers delta sync
|
||||||
await fetchOrders();
|
await fetchOrders();
|
||||||
} else {
|
} else {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -864,7 +922,7 @@
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
showToast(`Storniert: ${name}`, 'success');
|
showToast(`Storniert: ${name}`, 'success');
|
||||||
localStorage.removeItem('kantine_history_cache');
|
fullOrderHistoryCache = null; // Clear memory cache so next history open triggers delta sync
|
||||||
await fetchOrders();
|
await fetchOrders();
|
||||||
} else {
|
} else {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -888,6 +946,8 @@
|
|||||||
|
|
||||||
if (userFlags.size === 0) {
|
if (userFlags.size === 0) {
|
||||||
bellBtn.classList.add('hidden');
|
bellBtn.classList.add('hidden');
|
||||||
|
bellIcon.style.color = 'var(--text-secondary)';
|
||||||
|
bellIcon.style.textShadow = 'none';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,10 +982,10 @@
|
|||||||
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
|
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
|
||||||
|
|
||||||
if (anyAvailable) {
|
if (anyAvailable) {
|
||||||
bellIcon.style.color = 'var(--success-color)';
|
bellIcon.style.color = '#10b981'; // green / success
|
||||||
bellIcon.style.textShadow = '0 0 10px rgba(16, 185, 129, 0.4)';
|
bellIcon.style.textShadow = '0 0 10px rgba(16, 185, 129, 0.4)';
|
||||||
} else {
|
} else {
|
||||||
bellIcon.style.color = 'var(--warning-color)';
|
bellIcon.style.color = '#f59e0b'; // yellow / warning
|
||||||
bellIcon.style.textShadow = '0 0 10px rgba(245, 158, 11, 0.4)';
|
bellIcon.style.textShadow = '0 0 10px rgba(245, 158, 11, 0.4)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1100,6 +1160,7 @@
|
|||||||
console.log(`[Cache] Parsed ${allWeeks.length} weeks:`, allWeeks.map(w => `KW${w.weekNumber}/${w.year} (${(w.days || []).length} days)`));
|
console.log(`[Cache] Parsed ${allWeeks.length} weeks:`, allWeeks.map(w => `KW${w.weekNumber}/${w.year} (${(w.days || []).length} days)`));
|
||||||
renderVisibleWeeks();
|
renderVisibleWeeks();
|
||||||
updateNextWeekBadge();
|
updateNextWeekBadge();
|
||||||
|
updateAlarmBell();
|
||||||
if (cachedTs) updateLastUpdatedTime(cachedTs);
|
if (cachedTs) updateLastUpdatedTime(cachedTs);
|
||||||
console.log('Loaded menu from cache');
|
console.log('Loaded menu from cache');
|
||||||
return true;
|
return true;
|
||||||
@@ -1943,19 +2004,8 @@
|
|||||||
const dm = devToggle.checked;
|
const dm = devToggle.checked;
|
||||||
container.innerHTML = '<p style="color:var(--text-secondary);">Lade Versionen...</p>';
|
container.innerHTML = '<p style="color:var(--text-secondary);">Lade Versionen...</p>';
|
||||||
|
|
||||||
try {
|
function renderVersionsList(versions) {
|
||||||
let versions;
|
if (!versions || !versions.length) {
|
||||||
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) {
|
|
||||||
container.innerHTML = '<p style="color:var(--text-secondary);">Keine Versionen gefunden.</p>';
|
container.innerHTML = '<p style="color:var(--text-secondary);">Keine Versionen gefunden.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1987,6 +2037,34 @@
|
|||||||
`;
|
`;
|
||||||
list.appendChild(li);
|
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) {
|
} catch (e) {
|
||||||
container.innerHTML = `<p style="color:#e94560;">Fehler: ${e.message}</p>`;
|
container.innerHTML = `<p style="color:#e94560;">Fehler: ${e.message}</p>`;
|
||||||
}
|
}
|
||||||
|
|||||||
58
release.sh
Executable file
58
release.sh
Executable 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
|
||||||
@@ -801,7 +801,7 @@ body {
|
|||||||
/* No opacity/filter here - fully visible */
|
/* No opacity/filter here - fully visible */
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
border: 1px solid var(--accent-color);
|
border: 1px solid #8b5cf6;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: 0 -1rem 1.5rem -1rem;
|
margin: 0 -1rem 1.5rem -1rem;
|
||||||
|
|||||||
@@ -108,7 +108,8 @@ try {
|
|||||||
[/function\s+isNewer/, 'isNewer function'],
|
[/function\s+isNewer/, 'isNewer function'],
|
||||||
[/function\s+openVersionMenu/, 'openVersionMenu function'],
|
[/function\s+openVersionMenu/, 'openVersionMenu function'],
|
||||||
[/kantine_dev_mode/, 'dev-mode localStorage key'],
|
[/kantine_dev_mode/, 'dev-mode localStorage key'],
|
||||||
[/function\s+isCacheFresh/, 'isCacheFresh function']
|
[/function\s+isCacheFresh/, 'isCacheFresh function'],
|
||||||
|
[/limit=5/, 'Delta fetch limit parameter']
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const [regex, label] of checks) {
|
for (const [regex, label] of checks) {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v1.4.5
|
v1.4.13
|
||||||
|
|||||||
Reference in New Issue
Block a user