ajout mode plein air v1
This commit is contained in:
parent
11151bc91d
commit
cc92e2b5d7
3 changed files with 168 additions and 4 deletions
|
@ -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
|
||||
});
|
||||
|
|
|
@ -23,6 +23,20 @@
|
|||
<span>▶</span>
|
||||
}
|
||||
</label>
|
||||
|
||||
<div class="control-group" id="pleinAirMode">
|
||||
<label>
|
||||
<input type="checkbox" [(ngModel)]="pleinAirMode" (change)="togglePleinAir()">
|
||||
Mode plein air
|
||||
</label>
|
||||
@if (pleinAirMode) {
|
||||
<div class="quick-actions" style="margin-top:8px; display:flex; gap:6px; flex-wrap:wrap;">
|
||||
<button class="btn" (click)="quickCreate('traffic.contestation')">🚩 Contester</button>
|
||||
<button class="btn" (click)="quickCreate('traffic.interruption')">⛓️ Interruption</button>
|
||||
<button class="btn" (click)="quickCreate('traffic.wrong_way')">⛖ Détourné</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="filters-group">
|
||||
@if (showFilters) {
|
||||
|
@ -78,11 +92,12 @@
|
|||
<div class="control-group">
|
||||
<app-what-filter [label]="'Filtrer par type d\'événement'" [available]="availableWhatTypes" [selected]="selectedWhatFilter" (selectedChange)="selectedWhatFilter = $event; onWhatFilterChange()"></app-what-filter>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<app-osm></app-osm>
|
||||
<!-- <app-osm></app-osm>
|
||||
|
||||
<app-menu></app-menu>
|
||||
<app-menu></app-menu> -->
|
||||
<hr>
|
||||
}
|
||||
</div>
|
||||
|
@ -100,7 +115,9 @@
|
|||
<li> Mettre à jour un évènement: Sélectionnez un évènement sur la carte ou dans la liste pour le modifier.</li>
|
||||
</ul>
|
||||
</div>
|
||||
@if(!pleinAirMode){
|
||||
<app-edit-form [selected]="selected" (saved)="onSaved($event)" (created)="onCreated($event)" (deleted)="onDeleted($event)" (canceled)="onCanceled()"></app-edit-form>
|
||||
}
|
||||
}
|
||||
|
||||
<div id="fixed_actions">
|
||||
|
@ -174,6 +191,15 @@
|
|||
<button class="button toggle-options" (click)="showOptions = !showOptions">
|
||||
Options
|
||||
</button>
|
||||
@if (toasts.length) {
|
||||
<div class="toaster" style="position:fixed;right:16px;top:16px;display:flex;flex-direction:column;gap:8px;z-index:1000;">
|
||||
@for (t of toasts; track t.id) {
|
||||
<div class="toast" [class.success]="t.type==='success'" [class.error]="t.type==='error'" [class.info]="t.type==='info'" style="padding:10px 12px;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,0.15);background:#fff;min-width:200px;">
|
||||
{{t.message}}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (theme()) {
|
||||
<div class="subtheme-bar">
|
||||
<div class="help">Thème: {{ theme() }} — Cliquez sur la carte pour définir des coordonnées puis créez un évènement du sous-thème choisi.</div>
|
||||
|
|
|
@ -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<string | number> = [];
|
||||
|
@ -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<string>([
|
||||
'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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue