diff --git a/frontend/src/app/maps/all-events/all-events.ts b/frontend/src/app/maps/all-events/all-events.ts index acc04ed..003adc1 100644 --- a/frontend/src/app/maps/all-events/all-events.ts +++ b/frontend/src/app/maps/all-events/all-events.ts @@ -101,10 +101,58 @@ export class AllEvents implements OnInit, OnDestroy { const lat = parseFloat(this.route.snapshot.queryParams['lat']) || 48.8566; const lon = parseFloat(this.route.snapshot.queryParams['lon']) || 2.3522; const zoom = parseFloat(this.route.snapshot.queryParams['zoom']) || 8; + const basemap = (this.route.snapshot.queryParams['basemap'] || '').toLowerCase(); + + // Déterminer le style de fond de carte selon le query param "basemap" + // valeurs supportées: "vector" (défaut), "raster", "waymarked" + const style = (() => { + if (basemap === 'raster') { + return { + version: 8, + sources: { + osm: { + type: 'raster', + tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], + tileSize: 256, + attribution: '© OpenStreetMap contributors' + } + }, + layers: [ + { id: 'osm-tiles', type: 'raster', source: 'osm', minzoom: 0, maxzoom: 19 } + ] + } as any; + } + if (basemap === 'waymarked') { + return { + version: 8, + sources: { + osm: { + type: 'raster', + tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], + tileSize: 256, + attribution: '© OpenStreetMap contributors' + }, + waymarked: { + type: 'raster', + // Waymarked Trails randonnée (hiking) + tiles: ['https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png'], + tileSize: 256, + attribution: '© waymarkedtrails.org' + } + }, + layers: [ + { id: 'osm-tiles', type: 'raster', source: 'osm', minzoom: 0, maxzoom: 19 }, + { id: 'waymarked-overlay', type: 'raster', source: 'waymarked', minzoom: 0, maxzoom: 19 } + ] + } as any; + } + // vector (défaut) + return 'https://tiles.openfreemap.org/styles/liberty'; + })(); this.map = new maplibregl.Map({ container: this.mapContainer.nativeElement, - style: 'https://tiles.openfreemap.org/styles/liberty', + style, center: [lon, lat], zoom: zoom }); diff --git a/frontend/src/app/pages/home/home.html b/frontend/src/app/pages/home/home.html index c25a50a..2013fc3 100644 --- a/frontend/src/app/pages/home/home.html +++ b/frontend/src/app/pages/home/home.html @@ -23,6 +23,20 @@ } + +
+ + @if (pleinAirMode) { +
+ + + +
+ } +
@if (showFilters) { @@ -78,11 +92,12 @@
+ - +
}
@@ -100,7 +115,9 @@
  • Mettre à jour un évènement: Sélectionnez un évènement sur la carte ou dans la liste pour le modifier.
  • + @if(!pleinAirMode){ + } }
    @@ -174,6 +191,15 @@ + @if (toasts.length) { +
    + @for (t of toasts; track t.id) { +
    + {{t.message}} +
    + } +
    + } @if (theme()) {
    Thème: {{ theme() }} — Cliquez sur la carte pour définir des coordonnées puis créez un évènement du sous-thème choisi.
    diff --git a/frontend/src/app/pages/home/home.ts b/frontend/src/app/pages/home/home.ts index 0af7996..662e23a 100644 --- a/frontend/src/app/pages/home/home.ts +++ b/frontend/src/app/pages/home/home.ts @@ -41,6 +41,8 @@ export class Home implements OnInit, OnDestroy { showFilters = false; showEditForm = false; showOptions = false; + pleinAirMode = false; + toasts: Array<{ id: number, type: 'success' | 'error' | 'info', message: string }> = []; selectionMode: 'none' | 'rectangle' | 'polygon' = 'none'; selectedIds: Array = []; @@ -76,9 +78,16 @@ export class Home implements OnInit, OnDestroy { showUnlocatedList = false; ngOnInit() { + // Écouteur global pour toasts + try { + (window as any).addEventListener('toast', (e: any) => { + const d = e?.detail || {}; this.pushToast(d.type || 'info', d.message || ''); + }); + } catch {} this.route.queryParamMap.subscribe(map => { const id = (map.get('id') || '').trim(); const what = (map.get('what') || '').trim(); + const pleinAir = (map.get('pleinair') || '').trim().toLowerCase(); const limitParam = map.get('limit'); const limit = limitParam ? Number(limitParam) : null; // Charger selon les query params @@ -91,6 +100,10 @@ export class Home implements OnInit, OnDestroy { if (what) { this.selectedWhatFilter = what; } + // Activer mode plein air via query param + if (pleinAir === '1' || pleinAir === 'true' || pleinAir === 'yes') { + this.enablePleinAirMode(); + } }); this.startAutoReload(); } @@ -260,9 +273,49 @@ export class Home implements OnInit, OnDestroy { }); } + // Mode plein air: ne garder que certains types + if (this.pleinAirMode) { + const allowed = new Set([ + 'traffic.contestation', + 'traffic.interruption', + 'traffic.wrong_way' + ]); + filtered = filtered.filter(f => allowed.has(f?.properties?.what || '')); + } + this.filteredFeatures = filtered; } + togglePleinAir() { + this.pleinAirMode = !this.pleinAirMode; + this.applyFilters(); + } + + enablePleinAirMode() { + if (!this.pleinAirMode) { + this.pleinAirMode = true; + this.applyFilters(); + } + } + + // Actions rapides plein air + quickCreate(what: 'traffic.contestation' | 'traffic.interruption' | 'traffic.wrong_way') { + const osmUsername = this.osmAuth.getUsername(); + this.selected = { + id: null, + properties: { + label: '', + description: '', + what, + where: '', + ...(osmUsername && { last_modified_by: osmUsername }) + }, + geometry: { type: 'Point', coordinates: [0, 0] } + }; + // Ensuite, l'utilisateur clique sur la carte: voir onPickCoords() + this.showEditForm = true; + } + goToNewCategories() { this.router.navigate(['/nouvelles-categories']); } @@ -279,7 +332,35 @@ export class Home implements OnInit, OnDestroy { geometry: { type: 'Point', coordinates: [lon, lat] } }; this.showOptions = true; - + // En mode plein air, si c'est une création rapide, proposer l'envoi direct + if (this.pleinAirMode && (this.selected.id == null)) { + const w = this.selected.properties.what; + const allowed = new Set(['traffic.contestation','traffic.interruption','traffic.wrong_way']); + if (allowed.has(w)) { + const ok = typeof window !== 'undefined' ? window.confirm('Envoyer cet évènement maintenant ?') : true; + if (ok) { + const now = new Date(); + const stop = new Date(now.getTime() + 6 * 3600 * 1000); + const feature = { + type: 'Feature', + properties: { + type: 'unscheduled', + label: this.selected.properties.label || (oedb.presets.what as any)[w]?.label || 'Évènement', + description: this.selected.properties.description || (oedb.presets.what as any)[w]?.description || '', + what: w, + where: this.selected.properties.where || '', + start: now.toISOString(), + stop: stop.toISOString() + }, + geometry: { type: 'Point', coordinates: [lon, lat] } + } as any; + this.OedbApi.createEvent(feature).subscribe({ + next: () => { this.pushToast('success', 'Évènement créé'); this.selected = null; this.loadEvents(); }, + error: () => { this.pushToast('error', 'Échec de création'); } + }); + } + } + } } else { const osmUsername = this.osmAuth.getUsername(); const whatKey = this.activeSubtheme(); @@ -306,6 +387,15 @@ export class Home implements OnInit, OnDestroy { } } + private pushToast(type: 'success' | 'error' | 'info', message: string) { + if (!message) return; + const id = Date.now() + Math.random(); + this.toasts.push({ id, type, message }); + setTimeout(() => { + this.toasts = this.toasts.filter(t => t.id !== id); + }, 3000); + } + onSaved(_res: any) { // refresh list after update this.loadEvents();