diff --git a/frontend/src/app/forms/edit-form/edit-form.ts b/frontend/src/app/forms/edit-form/edit-form.ts index 21b0607..e28af89 100644 --- a/frontend/src/app/forms/edit-form/edit-form.ts +++ b/frontend/src/app/forms/edit-form/edit-form.ts @@ -363,6 +363,7 @@ export class EditForm implements OnChanges { onCancelEdit() { this.selected = null; + this.featureId.set(null); this.form.reset({ label: '', diff --git a/frontend/src/app/pages/home/home.html b/frontend/src/app/pages/home/home.html index 3f51329..244c8db 100644 --- a/frontend/src/app/pages/home/home.html +++ b/frontend/src/app/pages/home/home.html @@ -9,7 +9,7 @@ @if(showOptions){
- + @@ -81,7 +81,7 @@
} @@ -89,7 +89,7 @@
- + @if(showEditForm){

Guide

@@ -129,7 +129,7 @@ @if(selected !== null){
-

sélectionné: {{selected.properties.name}} {{selected.properties.title}} {{selected.properties.label}}

+

sélectionné: {{selected.properties.name}} {{selected.properties.title}} {{selected.properties.label}}

@@ -208,7 +208,7 @@ } - + @if (selectedIds.length) {
@@ -252,27 +252,63 @@
} - - - + + + }
- + @if(!showUnlocatedList){ + @if (pleinAirMode) { + @if (showQuickActions) { +
- - - + + +
+ } } + @if (civilianMode) { + @if (showQuickActions) { + +
+ +
+ } + } + @if (showGuidePresetPlace) { +

+ Sélectionnez l'endroit sur la carte +

+ } + @if (showGuidePresetMoreInfo) { +

+ Vous pouvez donner plus de détails + + + +

+ } + } + @if (toasts.length) {
+ style=""> @for (t of toasts; track t.id) {
@for (f of unlocatedOrOnline; track f.id) {
  • 📌
    @@ -327,15 +363,19 @@
    - @if (selected) { + @if (selected && showEditForm) {

    Détails

    - +
    @@ -379,6 +419,9 @@ - -
  • \ No newline at end of file + } +
    diff --git a/frontend/src/app/pages/home/home.scss b/frontend/src/app/pages/home/home.scss index 2068718..3427d53 100644 --- a/frontend/src/app/pages/home/home.scss +++ b/frontend/src/app/pages/home/home.scss @@ -176,12 +176,23 @@ } .fab.counter{ background: #0d9488; + margin-left: 0.5rem; } .fab.plus{ background: #1976d2; } } +.filters{ + label{ + padding: 1rem; + display: block; + cursor: pointer; + &:hover{ + background: lemonchiffon; + } + } +} .presets{ position: fixed; @@ -196,6 +207,15 @@ margin-left: 0; } } +.agenda-main{ + app-edit-form{ + right: 2rem; + top: 3.5rem; + .actions{ + right: 1rem !important; + } + } +} app-edit-form{ position: fixed; top: 135px; @@ -205,7 +225,7 @@ app-edit-form{ max-height: 77.7vh; display: block; overflow: auto; - background: rgba(228, 235, 255, 0.5); + background: rgba(228, 235, 255, 0.85); border: 1px solid rgba(0,0,0,0.06); border-radius: 10px; padding: 10px; @@ -249,9 +269,9 @@ app-edit-form{ &:focus{ outline: none; } - -} +} +// presets de boutons .quick-actions{ margin-top: 8px; display: inline-block; @@ -263,7 +283,38 @@ app-edit-form{ z-index: 100; } +.guide-preset{ + background: #4caf50; + color: #222; + padding: 1rem; + border-radius: 0.5rem; + margin: 1rem; + position: fixed; + z-index: 100; + bottom: 2rem; + right: 3.5rem; +} +.guide-preset-more{ + background: white; + padding: 1rem; + position: fixed; + z-index: 100; + bottom: 1rem; + margin: 1rem; + width: 70%; + input, textarea{ + width: 90%; + } + button{ + margin: 1rem; + display: block; + background: #4caf50; + color: white; + padding: 2rem 1rem; + border-radius: 0.5rem; + } +} table{ width: 100%; border-collapse: collapse; @@ -289,4 +340,20 @@ table{ th{ background: #f0f0f0; } -} \ No newline at end of file +} +.toaster{ + position:fixed; + right:16px; + top:16px; + display:flex; + flex-direction:column; + gap:8px; + z-index:1000; + + &.success{ + background: greenyellow; + } + &.info{ + background: cornflowerblue; + } +} diff --git a/frontend/src/app/pages/home/home.ts b/frontend/src/app/pages/home/home.ts index 7f1b6b3..3e7e26f 100644 --- a/frontend/src/app/pages/home/home.ts +++ b/frontend/src/app/pages/home/home.ts @@ -12,6 +12,7 @@ import { UnlocatedEvents } from '../../shared/unlocated-events/unlocated-events' import { OsmAuth } from '../../services/osm-auth'; import { Osm } from '../../forms/osm/osm'; import { WhatFilterComponent } from '../../shared/what-filter/what-filter'; +import {NgClass} from '@angular/common'; @Component({ selector: 'app-home', standalone: true, @@ -22,7 +23,8 @@ import { WhatFilterComponent } from '../../shared/what-filter/what-filter'; EditForm, Osm, FormsModule, - WhatFilterComponent + WhatFilterComponent, + NgClass ], templateUrl: './home.html', styleUrl: './home.scss' @@ -42,6 +44,7 @@ export class Home implements OnInit, OnDestroy { showEditForm = false; showOptions = true; pleinAirMode = false; + civilianMode = false; toasts: Array<{ id: number, type: 'success' | 'error' | 'info', message: string }> = []; selectionMode: 'none' | 'rectangle' | 'polygon' = 'none'; @@ -51,7 +54,7 @@ export class Home implements OnInit, OnDestroy { batchFieldKey = ''; batchFieldValue: any = ''; batchSummary: { success: number; failed: number; networkErrors: number } | null = null; - + // Nouvelles propriétés pour le rechargement automatique et la sélection de jours autoReloadEnabled = true; autoReloadInterval: any = null; @@ -71,7 +74,7 @@ export class Home implements OnInit, OnDestroy { // Option bbox useBboxFilter = true; currentBbox: { minLng: number, minLat: number, maxLng: number, maxLat: number } | null = null; - + // Bbox par défaut pour l'Île-de-France private readonly IDF_BBOX = { minLng: 1.4, @@ -80,10 +83,11 @@ export class Home implements OnInit, OnDestroy { maxLat: 49.2 }; // Debounce pour la recherche - private searchDebounceTimer: any = null; + protected searchDebounceTimer: any = null; // Non localisés / en ligne unlocatedOrOnline: Array = []; showUnlocatedList = false; + protected showQuickActions: boolean = true; ngOnInit() { // Écouteur global pour toasts @@ -92,10 +96,10 @@ export class Home implements OnInit, OnDestroy { const d = e?.detail || {}; this.pushToast(d.type || 'info', d.message || ''); }); } catch {} - + // Initialiser la bbox par défaut pour l'Île-de-France this.currentBbox = { ...this.IDF_BBOX }; - + this.route.queryParamMap.subscribe(map => { const id = (map.get('id') || '').trim(); const what = (map.get('what') || 'culture').trim(); @@ -239,11 +243,11 @@ export class Home implements OnInit, OnDestroy { this.theme.set(t || null); this.buildSubthemes(); }); - + // Ajouter les catégories principales whatTypes.add('culture'); whatTypes.add('traffic'); - + this.availableWhatTypes = Array.from(whatTypes).sort(); } @@ -252,7 +256,7 @@ export class Home implements OnInit, OnDestroy { if (this.searchDebounceTimer) { clearTimeout(this.searchDebounceTimer); } - + // Créer un nouveau timer de 500ms this.searchDebounceTimer = setTimeout(() => { this.applyFilters(); @@ -311,6 +315,7 @@ export class Home implements OnInit, OnDestroy { } enablePleinAirMode() { + this.pushToast('info', "mode plein air activé") if (!this.pleinAirMode) { this.pleinAirMode = true; this.applyFilters(); @@ -318,8 +323,10 @@ export class Home implements OnInit, OnDestroy { } // Actions rapides plein air - quickCreate(what: 'traffic.contestation' | 'traffic.interruption' | 'traffic.wrong_way') { + quickCreate(what: string) { const osmUsername = this.osmAuth.getUsername(); + this.selectedPreset = what; + this.showGuidePresetPlace = true; this.selected = { id: null, properties: { @@ -332,7 +339,40 @@ export class Home implements OnInit, OnDestroy { geometry: { type: 'Point', coordinates: [0, 0] } }; // Ensuite, l'utilisateur clique sur la carte: voir onPickCoords() - this.showEditForm = true; + // this.showEditForm = true; + } + submitPreset(){ + const now = new Date(); + const w = this.selected.properties.what; + // fin du signalement par défaut dans 20 jours + const stop = new Date(now.getTime() + 20* 24 * 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, + reporter: this.guidePresetMoreInfoPseudo, + 'reporter:description': this.presetMoreDetails, + where: this.selected.properties.where || '', + start: now.toISOString(), + stop: stop.toISOString() + }, + geometry: { type: 'Point', coordinates: [this.selected.lon, this.selected.lat] } + } as any; + this.OedbApi.createEvent(feature).subscribe({ + next: () => { + this.pushToast('success', 'Évènement créé'); + this.selected = null; + this.presetMoreDetails = '' + this.showGuidePresetMoreInfo = false + // Après création rapide en plein air: recharger uniquement ce type pour feedback instantané + this.selectedWhatFilter = w; + this.loadEvents({ what: 'traffic' }); + }, + error: () => { this.pushToast('error', 'Échec de création'); } + }); } goToNewCategories() { @@ -342,6 +382,10 @@ export class Home implements OnInit, OnDestroy { onSelect(feature: any) { this.selected = feature; } + onSelectFromCalendarView(feature: any) { + this.selected = feature; + this.showEditForm = false; + } onPickCoords(coords: [number, number]) { const [lon, lat] = coords; @@ -350,39 +394,20 @@ export class Home implements OnInit, OnDestroy { ...this.selected, geometry: { type: 'Point', coordinates: [lon, lat] } }; - this.showOptions = true; + // 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)) { + + this.showGuidePresetPlace = true; + this.showGuidePresetMoreInfo = true; + this.showQuickActions = false; + const self:this = this; 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; - // Après création rapide en plein air: recharger uniquement ce type pour feedback instantané - this.selectedWhatFilter = w; - this.loadEvents({ what: 'traffic' }); - }, - error: () => { this.pushToast('error', 'Échec de création'); } - }); + self.submitPreset() } } } @@ -418,7 +443,7 @@ export class Home implements OnInit, OnDestroy { this.toasts.push({ id, type, message }); setTimeout(() => { this.toasts = this.toasts.filter(t => t.id !== id); - }, 3000); + }, 6000); } onSaved(_res: any) { @@ -632,6 +657,12 @@ export class Home implements OnInit, OnDestroy { } // Méthode pour recharger les événements quand la carte bouge + showGuidePresetPlace: boolean = false; + showGuidePresetMoreInfo: boolean= false; + + guidePresetMoreInfoPseudo: string = ''; + presetMoreDetails: string = ''; + selectedPreset: string = ''; onMapMove(bbox: { minLng: number, minLat: number, maxLng: number, maxLat: number }) { this.setCurrentBbox(bbox); }