Feature: Parallele Bild-Uploads, Heartbeat last_seen, Admin zuletzt aktiv, SW by-v1071

- Tagebuch: Bilder werden parallel hochgeladen (Promise.all), Button zeigt Fortschritt
- Auth: /heartbeat Route ergänzt — aktualisiert last_seen alle 5 Min
- Admin: last_seen + last_login in Nutzer-Liste angezeigt (🟢/🔵/)
- Bump SW by-v1071
This commit is contained in:
rene 2026-05-25 20:26:58 +02:00
parent 9677d1e71a
commit 3abf974d29
8 changed files with 44 additions and 25 deletions

View file

@ -101,9 +101,9 @@
</script>
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
<link rel="stylesheet" href="/css/design-system.css?v=1070">
<link rel="stylesheet" href="/css/layout.css?v=1070">
<link rel="stylesheet" href="/css/components.css?v=1070">
<link rel="stylesheet" href="/css/design-system.css?v=1071">
<link rel="stylesheet" href="/css/layout.css?v=1071">
<link rel="stylesheet" href="/css/components.css?v=1071">
</head>
<body>
@ -616,10 +616,10 @@
<div id="modal-container"></div>
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
<script src="/js/api.js?v=1070"></script>
<script src="/js/ui.js?v=1070"></script>
<script src="/js/app.js?v=1070"></script>
<script src="/js/worlds.js?v=1070"></script>
<script src="/js/api.js?v=1071"></script>
<script src="/js/ui.js?v=1071"></script>
<script src="/js/app.js?v=1071"></script>
<script src="/js/worlds.js?v=1071"></script>
<!-- Feature-Seiten werden lazy geladen -->

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '1070'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '1071'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
const IS_STAGING = location.hostname === 'staging.banyaro.app';
// Cache-Bust-Parameter nach Update-Reload sofort entfernen.

View file

@ -820,7 +820,13 @@ window.Page_admin = (() => {
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">
🗺 ${u.route_count} Routen · ${u.total_km} km
· 📍 ${u.poi_count} POIs
${u.last_route ? '· zuletzt ' + new Date(u.last_route).toLocaleDateString('de-DE') : ''}
</div>
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">
${u.last_seen
? '🟢 zuletzt aktiv ' + new Date(u.last_seen).toLocaleString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'})
: u.last_login
? '🔵 zuletzt eingeloggt ' + new Date(u.last_login).toLocaleString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'})
: '⚪ nie aktiv'}
</div>
</div>

View file

@ -1722,29 +1722,35 @@ window.Page_diary = (() => {
};
async function _uploadNewFiles(entryId) {
let failCount = 0;
const uploaded = [];
let exifGps = null;
for (const file of _newFiles) {
const total = _newFiles.length;
const saveBtn = document.querySelector('button[form="diary-form"]');
let done = 0;
if (saveBtn) saveBtn.textContent = `0 von ${total} hochgeladen…`;
const results = await Promise.all(_newFiles.map(async file => {
const formData = new FormData();
formData.append('file', file);
try {
const formData = new FormData();
formData.append('file', file);
const m = await API.diary.uploadMedia(_appState.activeDog.id, entryId, formData);
uploaded.push(m);
if (m.exif_lat != null && m.exif_lon != null && !exifGps) {
exifGps = { lat: m.exif_lat, lon: m.exif_lon };
}
if (saveBtn) saveBtn.textContent = `${++done} von ${total} hochgeladen…`;
return { ok: true, m };
} catch {
failCount++;
if (saveBtn) saveBtn.textContent = `${++done} von ${total} hochgeladen…`;
return { ok: false };
}
}
}));
const uploaded = results.filter(r => r.ok).map(r => r.m);
const failCount = results.filter(r => !r.ok).length;
const exifGps = results.find(r => r.ok && r.m.exif_lat != null)?.m;
if (failCount > 0) {
UI.toast.warning(`${failCount} Medium${failCount > 1 ? 'en' : ''} konnte${failCount > 1 ? 'n' : ''} nicht hochgeladen werden.`);
}
if (exifGps) {
UI.toast.success(`📍 Standort aus Foto-GPS übernommen`);
}
return { uploaded, exifGps };
return { uploaded, exifGps: exifGps ? { lat: exifGps.exif_lat, lon: exifGps.exif_lon } : null };
}
if (isEdit) {

View file

@ -4,7 +4,7 @@
============================================================ */
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
const VER = '1070';
const VER = '1071';
const CACHE_VERSION = `by-v${VER}`;
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten