Sprint 10: OSM-POI-Cache, Karten-Clustering, Routen-Redesign
Karte (map.js):
- OSM Overpass API: Restaurants, Tierärzte, Parkplätze, Bänke, Wasserstellen
- Leaflet.markercluster für alle OSM-Layer
- Standort-Dot mit GPS-Genauigkeitskreis, Wake-Lock bei Aufzeichnung
- Community-Pins setzen/löschen, Meldungen, Crosshair-Placement
- Layer-Sichtbarkeit in localStorage (by_map_visible_v1)
Routen (routes.js + routen.py):
- Komoot-Stil: SVG-Track-Preview, Foto-Upload, Nearby-POIs im Detail-Modal
- Neue Felder: is_public, hunde_tauglichkeit, foto_urls
- Rate-Endpoint (POST /api/routes/{id}/rate)
- Foto-Upload (POST /api/routes/{id}/photo)
- Fix: json_extract $[-1] → $[#-1] (SQLite-kompatibler Pfad für letztes Element)
Backend (osm.py, database.py, scheduler.py):
- /api/osm/pois: OSM-Overpass-Cache mit Tile-Logik (14 Tage TTL)
- /api/osm/user-poi: Community-Marker CRUD
- /api/osm/report: Marker als ungültig melden
- Neue Tabellen: osm_pois, osm_tiles, user_map_pois, osm_reports
- Giftköder-Archiv-Job (täglich 03:00, soft-delete nach Ablauf)
- Giftköder-Archiv-Job als APScheduler-CronJob
UI: Orte-Menüpunkt entfernt (in Karte integriert), APP_VER auf 62
This commit is contained in:
parent
bf26e5faf4
commit
ebe4ce20cf
16 changed files with 3020 additions and 737 deletions
|
|
@ -3,6 +3,8 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '62'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
|
||||
const App = (() => {
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
|
@ -26,8 +28,7 @@ const App = (() => {
|
|||
'dog-profile': { title: 'Mein Hund', module: null },
|
||||
map: { title: 'Karte', module: null },
|
||||
routes: { title: 'Routen', module: null },
|
||||
places: { title: 'Orte', module: null },
|
||||
events: { title: 'Events', module: null },
|
||||
events: { title: 'Events', module: null },
|
||||
poison: { title: 'Giftköder-Alarm', module: null },
|
||||
walks: { title: 'Gassi-Treffen', module: null },
|
||||
sitting: { title: 'Sitting', module: null },
|
||||
|
|
@ -119,10 +120,12 @@ const App = (() => {
|
|||
}
|
||||
|
||||
function _loadScript(src) {
|
||||
// Versionierter URL damit SW/Browser-Cache bei jedem Deploy invalidiert wird
|
||||
const versioned = `${src}?v=${APP_VER}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (document.querySelector(`script[src="${src}"]`)) { resolve(); return; }
|
||||
if (document.querySelector(`script[src="${versioned}"]`)) { resolve(); return; }
|
||||
const s = document.createElement('script');
|
||||
s.src = src;
|
||||
s.src = versioned;
|
||||
s.onload = resolve;
|
||||
s.onerror = reject;
|
||||
document.head.appendChild(s);
|
||||
|
|
@ -193,11 +196,7 @@ const App = (() => {
|
|||
}
|
||||
|
||||
function _openSidebar() {
|
||||
const s = document.getElementById('sidebar');
|
||||
if (!s) { UI.toast.error('sidebar: NULL'); return; }
|
||||
s.classList.add('open');
|
||||
const cs = getComputedStyle(s);
|
||||
UI.toast.info(`display:${cs.display} | transform:${cs.transform} | z:${cs.zIndex}`);
|
||||
document.getElementById('sidebar')?.classList.add('open');
|
||||
document.getElementById('sidebar-backdrop')?.classList.add('visible');
|
||||
}
|
||||
|
||||
|
|
@ -223,10 +222,7 @@ const App = (() => {
|
|||
<button class="btn btn-danger w-full" data-quick="poison">
|
||||
⚠️ Giftköder melden
|
||||
</button>
|
||||
<button class="btn btn-secondary w-full" data-quick="place">
|
||||
📍 Ort hinzufügen
|
||||
</button>
|
||||
<button class="btn btn-nature w-full" data-quick="walk">
|
||||
<button class="btn btn-nature w-full" data-quick="walk">
|
||||
🦮 Gassi-Treffen erstellen
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -246,8 +242,7 @@ const App = (() => {
|
|||
if (action === 'diary') { navigate('diary'); pages['diary'].module?.openNew?.(); }
|
||||
if (action === 'health') { navigate('health'); pages['health'].module?.openNew?.(); }
|
||||
if (action === 'poison') { navigate('poison'); pages['poison'].module?.openNew?.(); }
|
||||
if (action === 'place') { navigate('places'); pages['places'].module?.openNew?.(); }
|
||||
if (action === 'walk') { navigate('walks'); pages['walks'].module?.openNew?.(); }
|
||||
if (action === 'walk') { navigate('walks'); pages['walks'].module?.openNew?.(); }
|
||||
}, 350);
|
||||
}, { once: true });
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue