more style for interface; dark top
This commit is contained in:
parent
e7a2d93d18
commit
2f9b91f2c7
4 changed files with 339 additions and 139 deletions
|
@ -301,6 +301,7 @@
|
|||
<input type="text" [(ngModel)]="guidePresetMoreInfoPseudo" placeholder="votre pseudo ou nom">
|
||||
<textarea [(ngModel)]="presetMoreDetails" placeholder="tronc d'arbre dans le chemin"></textarea>
|
||||
<button class="btn btn-primary" (click)="submitPreset()">Envoyer</button>
|
||||
<button class="btn btn-secondary" (click)="cancelSubmitPreset()">Annuler</button>
|
||||
</p>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
:host {
|
||||
display: block;
|
||||
|
||||
button{
|
||||
button {
|
||||
background: white;
|
||||
border: 1px solid rgba(0,0,0,0.06);
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
+ button{
|
||||
|
||||
+ button {
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,24 +25,26 @@
|
|||
grid-template-columns: 400px 1fr;
|
||||
grid-template-rows: 100vh;
|
||||
gap: 0;
|
||||
&.is-small{
|
||||
|
||||
&.is-small {
|
||||
grid-template-columns: 100px 1fr;
|
||||
}
|
||||
|
||||
/* Quand la zone principale est en plein écran */
|
||||
&:has(.main.is-full){
|
||||
&:has(.main.is-full) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.aside {
|
||||
background: #ffffff;
|
||||
border-right: 1px solid rgba(0,0,0,0.06);
|
||||
box-shadow: 2px 0 12px rgba(0,0,0,0.03);
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.03);
|
||||
padding: 16px;
|
||||
padding-bottom: 150px;
|
||||
overflow: auto;
|
||||
/* Masquer l'aside en mode plein écran */
|
||||
.layout:has(.main.is-full) &{
|
||||
.layout:has(.main.is-full) & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +54,8 @@
|
|||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
&.is-full{
|
||||
|
||||
&.is-full {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +108,7 @@
|
|||
&:focus {
|
||||
outline: none;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
|
||||
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,28 +134,82 @@
|
|||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
.unlocated-layout{
|
||||
|
||||
.unlocated-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 360px 1fr;
|
||||
height: 100%;
|
||||
}
|
||||
.agenda-sidebar{
|
||||
|
||||
.agenda-sidebar {
|
||||
overflow: auto;
|
||||
border-right: 1px solid #eee;
|
||||
padding: 10px;
|
||||
}
|
||||
.event-list{ list-style: none; padding: 0; margin: 0; }
|
||||
.event-item{ display: flex; gap: 8px; padding: 8px; cursor: pointer; }
|
||||
.event-item.active{ background: #f0f9ff; }
|
||||
.event-title{ font-weight: 600; }
|
||||
.event-when{ font-size: 12px; color: #64748b; }
|
||||
.agenda-main{ padding: 10px; }
|
||||
.event-edit-panel{ border: 1px solid #e2e8f0; border-radius: 8px; background: #fff; }
|
||||
.panel-header{ display:flex; justify-content: space-between; align-items:center; padding: 8px 12px; border-bottom: 1px solid #e2e8f0; }
|
||||
.panel-content{ padding: 8px; }
|
||||
.btn-close{ background: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; width: 32px; height: 32px; cursor: pointer; }
|
||||
.hint{ color: #64748b; padding: 20px; }
|
||||
.floating-actions{
|
||||
|
||||
.event-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.event-item {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.event-item.active {
|
||||
background: #f0f9ff;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.event-when {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.agenda-main {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.event-edit-panel {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #64748b;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.floating-actions {
|
||||
position: fixed;
|
||||
right: 1rem;
|
||||
bottom: 16px;
|
||||
|
@ -159,12 +218,12 @@
|
|||
gap: 10px;
|
||||
z-index: 2000;
|
||||
|
||||
.fab{
|
||||
.fab {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -174,27 +233,30 @@
|
|||
background: #1976d2;
|
||||
color: #fff;
|
||||
}
|
||||
.fab.counter{
|
||||
|
||||
.fab.counter {
|
||||
background: #0d9488;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
.fab.plus{
|
||||
|
||||
.fab.plus {
|
||||
background: #1976d2;
|
||||
}
|
||||
}
|
||||
|
||||
.filters{
|
||||
label{
|
||||
.filters {
|
||||
label {
|
||||
padding: 1rem;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
background: lemonchiffon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.presets{
|
||||
.presets {
|
||||
position: fixed;
|
||||
top: 63px;
|
||||
margin-left: 397px;
|
||||
|
@ -203,20 +265,23 @@
|
|||
display: block;
|
||||
|
||||
/* En plein écran, plus d'offset gauche */
|
||||
.layout:has(.main.is-full) &{
|
||||
.layout:has(.main.is-full) & {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
.agenda-main{
|
||||
app-edit-form{
|
||||
|
||||
.agenda-main {
|
||||
app-edit-form {
|
||||
right: 2rem;
|
||||
top: 3.5rem;
|
||||
.actions{
|
||||
|
||||
.actions {
|
||||
right: 1rem !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
app-edit-form{
|
||||
|
||||
app-edit-form {
|
||||
position: fixed;
|
||||
top: 135px;
|
||||
margin-left: 397px;
|
||||
|
@ -226,14 +291,14 @@ app-edit-form{
|
|||
display: block;
|
||||
overflow: auto;
|
||||
background: rgba(228, 235, 255, 0.85);
|
||||
border: 1px solid rgba(0,0,0,0.06);
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
padding-bottom: 150px;
|
||||
/* En plein écran, plus d'offset gauche */
|
||||
.layout:has(.main.is-full) &{
|
||||
.layout:has(.main.is-full) & {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
@ -243,36 +308,63 @@ app-edit-form{
|
|||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
.help { font-size: 12px; color: #64748b; }
|
||||
.chips { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.chip { border: 1px solid #e2e8f0; border-radius: 999px; padding: 6px 10px; background: #fff; cursor: pointer; }
|
||||
.chip.active { background: #e3f2fd; border-color: #90caf9; }
|
||||
.emoji { margin-right: 6px; }
|
||||
|
||||
.help {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.chips {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.chip {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 999px;
|
||||
padding: 6px 10px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chip.active {
|
||||
background: #e3f2fd;
|
||||
border-color: #90caf9;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-options{
|
||||
.toggle-options {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
z-index: 100;
|
||||
background: white;
|
||||
border: 1px solid rgba(0,0,0,0.06);
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
padding: 2rem 1rem;
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
&:active{
|
||||
|
||||
&:active {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
&:focus{
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// presets de boutons
|
||||
.quick-actions{
|
||||
.quick-actions {
|
||||
margin-top: 8px;
|
||||
display: inline-block;
|
||||
gap: 6px;
|
||||
|
@ -281,9 +373,16 @@ app-edit-form{
|
|||
right: 5rem;
|
||||
bottom: 1.1rem;
|
||||
z-index: 100;
|
||||
|
||||
.btn {
|
||||
|
||||
padding: 1rem 1.25rem;
|
||||
cursor: pointer;
|
||||
height: 3.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.guide-preset{
|
||||
.guide-preset {
|
||||
background: #4caf50;
|
||||
color: #222;
|
||||
padding: 1rem;
|
||||
|
@ -294,7 +393,8 @@ app-edit-form{
|
|||
bottom: 2rem;
|
||||
right: 3.5rem;
|
||||
}
|
||||
.guide-preset-more{
|
||||
|
||||
.guide-preset-more {
|
||||
background: white;
|
||||
padding: 1rem;
|
||||
position: fixed;
|
||||
|
@ -302,11 +402,13 @@ app-edit-form{
|
|||
bottom: 1rem;
|
||||
margin: 1rem;
|
||||
width: 70%;
|
||||
input, textarea{
|
||||
|
||||
input, textarea {
|
||||
width: 90%;
|
||||
|
||||
}
|
||||
button{
|
||||
|
||||
button {
|
||||
margin: 1rem;
|
||||
display: block;
|
||||
background: #4caf50;
|
||||
|
@ -315,45 +417,57 @@ app-edit-form{
|
|||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
table{
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
td:nth-of-type(1){
|
||||
|
||||
td:nth-of-type(1) {
|
||||
font-weight: bold;
|
||||
width: 100px;
|
||||
}
|
||||
th, td{
|
||||
|
||||
th, td {
|
||||
padding: 10px;
|
||||
}
|
||||
tr:nth-child(even){
|
||||
|
||||
tr:nth-child(even) {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
tr:nth-child(odd){
|
||||
|
||||
tr:nth-child(odd) {
|
||||
background: #fff;
|
||||
}
|
||||
tr:hover{
|
||||
|
||||
tr:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
th{
|
||||
|
||||
th {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
}
|
||||
.toaster{
|
||||
position:fixed;
|
||||
right:16px;
|
||||
top:16px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:8px;
|
||||
z-index:1000;
|
||||
|
||||
&.success{
|
||||
.toaster {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
z-index: 1000;
|
||||
|
||||
&.success {
|
||||
background: greenyellow;
|
||||
}
|
||||
&.info{
|
||||
|
||||
&.info {
|
||||
background: cornflowerblue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import { Component, inject, signal , OnDestroy, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import {Component, inject, signal, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {Menu} from './menu/menu';
|
||||
import { AllEvents } from '../../maps/all-events/all-events';
|
||||
import { EditForm } from '../../forms/edit-form/edit-form';
|
||||
import { OedbApi } from '../../services/oedb-api';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import {AllEvents} from '../../maps/all-events/all-events';
|
||||
import {EditForm} from '../../forms/edit-form/edit-form';
|
||||
import {OedbApi} from '../../services/oedb-api';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import oedb from '../../../oedb-types';
|
||||
|
||||
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 {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,
|
||||
|
@ -42,7 +43,7 @@ export class Home implements OnInit, OnDestroy {
|
|||
showTable = false;
|
||||
showFilters = false;
|
||||
showEditForm = false;
|
||||
showOptions = true;
|
||||
showOptions = false;
|
||||
pleinAirMode = false;
|
||||
civilianMode = false;
|
||||
toasts: Array<{ id: number, type: 'success' | 'error' | 'info', message: string }> = [];
|
||||
|
@ -88,17 +89,20 @@ export class Home implements OnInit, OnDestroy {
|
|||
unlocatedOrOnline: Array<any> = [];
|
||||
showUnlocatedList = false;
|
||||
protected showQuickActions: boolean = true;
|
||||
firstToastDone = 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 || '');
|
||||
const d = e?.detail || {};
|
||||
this.pushToast(d.type || 'info', d.message || '');
|
||||
});
|
||||
} catch {}
|
||||
} catch {
|
||||
}
|
||||
|
||||
// Initialiser la bbox par défaut pour l'Île-de-France
|
||||
this.currentBbox = { ...this.IDF_BBOX };
|
||||
this.currentBbox = {...this.IDF_BBOX};
|
||||
|
||||
this.route.queryParamMap.subscribe(map => {
|
||||
const id = (map.get('id') || '').trim();
|
||||
|
@ -111,24 +115,31 @@ export class Home implements OnInit, OnDestroy {
|
|||
if (id) {
|
||||
this.loadSingleEvent(id);
|
||||
} else {
|
||||
this.loadEvents({ what: what || undefined, limit: limit || undefined });
|
||||
this.loadEvents({what: what || undefined, limit: limit || undefined});
|
||||
}
|
||||
// Appliquer filtre par what côté client si fourni
|
||||
if (what) {
|
||||
this.selectedWhatFilter = what;
|
||||
}
|
||||
// Activer mode plein air via query param
|
||||
if (pleinAir === '1' || pleinAir === 'true' || pleinAir === 'yes') {
|
||||
this.enablePleinAirMode();
|
||||
}
|
||||
// if (pleinAir === '1' || pleinAir === 'true' || pleinAir === 'yes') {
|
||||
// this.enablePleinAirMode();
|
||||
// }
|
||||
// Support: preset=plein_air
|
||||
if (preset === 'plein_air') {
|
||||
if (!this.firstToastDone) {
|
||||
this.selectedWhatFilter = "traffic"
|
||||
this.pushToast('info', "mode plein air activé")
|
||||
this.useBboxFilter = true;
|
||||
this.loadEvents({ what: 'traffic' });
|
||||
}
|
||||
this.firstToastDone = true
|
||||
this.enablePleinAirMode();
|
||||
}
|
||||
});
|
||||
this.startAutoReload();
|
||||
|
||||
this.loadEvents({ what: "culture" , limit: 100 });
|
||||
this.loadEvents({what: "culture", limit: 100});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
@ -227,7 +238,7 @@ export class Home implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onDaysAheadChange() {
|
||||
this.loadEvents({ daysAhead: this.daysAhead, what: this.selectedWhatFilter || undefined });
|
||||
this.loadEvents({daysAhead: this.daysAhead, what: this.selectedWhatFilter || undefined});
|
||||
}
|
||||
|
||||
updateAvailableWhatTypes() {
|
||||
|
@ -278,8 +289,8 @@ export class Home implements OnInit, OnDestroy {
|
|||
const description = feature?.properties?.description || '';
|
||||
const what = feature?.properties?.what || '';
|
||||
return label.toLowerCase().includes(searchLower) ||
|
||||
description.toLowerCase().includes(searchLower) ||
|
||||
what.toLowerCase().includes(searchLower);
|
||||
description.toLowerCase().includes(searchLower) ||
|
||||
what.toLowerCase().includes(searchLower);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -315,7 +326,7 @@ export class Home implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
enablePleinAirMode() {
|
||||
this.pushToast('info', "mode plein air activé")
|
||||
|
||||
if (!this.pleinAirMode) {
|
||||
this.pleinAirMode = true;
|
||||
this.applyFilters();
|
||||
|
@ -326,7 +337,7 @@ export class Home implements OnInit, OnDestroy {
|
|||
quickCreate(what: string) {
|
||||
const osmUsername = this.osmAuth.getUsername();
|
||||
this.selectedPreset = what;
|
||||
this.showGuidePresetPlace = true;
|
||||
this.showGuidePresetPlace = true;
|
||||
this.selected = {
|
||||
id: null,
|
||||
properties: {
|
||||
|
@ -334,18 +345,25 @@ export class Home implements OnInit, OnDestroy {
|
|||
description: '',
|
||||
what,
|
||||
where: '',
|
||||
...(osmUsername && { last_modified_by: osmUsername })
|
||||
...(osmUsername && {last_modified_by: osmUsername})
|
||||
},
|
||||
geometry: { type: 'Point', coordinates: [0, 0] }
|
||||
geometry: {type: 'Point', coordinates: [0, 0]}
|
||||
};
|
||||
// Ensuite, l'utilisateur clique sur la carte: voir onPickCoords()
|
||||
// this.showEditForm = true;
|
||||
}
|
||||
submitPreset(){
|
||||
cancelSubmitPreset(){
|
||||
this.selected = null;
|
||||
this.presetMoreDetails = ''
|
||||
this.showGuidePresetMoreInfo = false
|
||||
this.showGuidePresetPlace = false;
|
||||
this.showQuickActions = 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 stop = new Date(now.getTime() + 20 * 24 * 3600 * 1000);
|
||||
const feature = {
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
|
@ -359,7 +377,7 @@ export class Home implements OnInit, OnDestroy {
|
|||
start: now.toISOString(),
|
||||
stop: stop.toISOString()
|
||||
},
|
||||
geometry: { type: 'Point', coordinates: [this.selected.lon, this.selected.lat] }
|
||||
geometry: {type: 'Point', coordinates: [this.selected.lon, this.selected.lat]}
|
||||
} as any;
|
||||
this.OedbApi.createEvent(feature).subscribe({
|
||||
next: () => {
|
||||
|
@ -367,11 +385,14 @@ export class Home implements OnInit, OnDestroy {
|
|||
this.selected = null;
|
||||
this.presetMoreDetails = ''
|
||||
this.showGuidePresetMoreInfo = false
|
||||
this.showGuidePresetPlace = false;
|
||||
// Après création rapide en plein air: recharger uniquement ce type pour feedback instantané
|
||||
this.selectedWhatFilter = w;
|
||||
this.loadEvents({ what: 'traffic' });
|
||||
this.loadEvents({what: 'traffic'});
|
||||
},
|
||||
error: () => { this.pushToast('error', 'Échec de création'); }
|
||||
error: () => {
|
||||
this.pushToast('error', 'Échec de création');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -382,6 +403,7 @@ export class Home implements OnInit, OnDestroy {
|
|||
onSelect(feature: any) {
|
||||
this.selected = feature;
|
||||
}
|
||||
|
||||
onSelectFromCalendarView(feature: any) {
|
||||
this.selected = feature;
|
||||
this.showEditForm = false;
|
||||
|
@ -392,19 +414,19 @@ export class Home implements OnInit, OnDestroy {
|
|||
if (this.selected && this.selected.properties) {
|
||||
this.selected = {
|
||||
...this.selected,
|
||||
geometry: { type: 'Point', coordinates: [lon, lat] }
|
||||
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']);
|
||||
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;
|
||||
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) {
|
||||
self.submitPreset()
|
||||
|
@ -430,9 +452,9 @@ export class Home implements OnInit, OnDestroy {
|
|||
description: '',
|
||||
what: whatKey || '',
|
||||
where: '',
|
||||
...(osmUsername && { last_modified_by: osmUsername })
|
||||
...(osmUsername && {last_modified_by: osmUsername})
|
||||
},
|
||||
geometry: { type: 'Point', coordinates: [lon, lat] }
|
||||
geometry: {type: 'Point', coordinates: [lon, lat]}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -440,7 +462,7 @@ 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 });
|
||||
this.toasts.push({id, type, message});
|
||||
setTimeout(() => {
|
||||
this.toasts = this.toasts.filter(t => t.id !== id);
|
||||
}, 6000);
|
||||
|
@ -469,9 +491,11 @@ export class Home implements OnInit, OnDestroy {
|
|||
startRectSelection() {
|
||||
this.selectionMode = this.selectionMode === 'rectangle' ? 'none' : 'rectangle';
|
||||
}
|
||||
|
||||
startPolySelection() {
|
||||
this.selectionMode = this.selectionMode === 'polygon' ? 'none' : 'polygon';
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
this.selectionMode = 'none';
|
||||
this.selectedIds = [];
|
||||
|
@ -490,12 +514,21 @@ export class Home implements OnInit, OnDestroy {
|
|||
|
||||
const doUpdate = async (id: string | number, updater: (f: any) => any) => {
|
||||
const feature = this.features.find(f => (f?.properties?.id ?? f?.id) === id);
|
||||
if (!feature) { failed++; return; }
|
||||
if (!feature) {
|
||||
failed++;
|
||||
return;
|
||||
}
|
||||
const updated = updater(feature);
|
||||
await new Promise<void>((resolve) => {
|
||||
this.OedbApi.updateEvent(id, updated).subscribe({
|
||||
next: () => { success++; resolve(); },
|
||||
error: (err) => { (err?.status === 0 ? networkErrors++ : failed++); resolve(); }
|
||||
next: () => {
|
||||
success++;
|
||||
resolve();
|
||||
},
|
||||
error: (err) => {
|
||||
(err?.status === 0 ? networkErrors++ : failed++);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -503,24 +536,35 @@ export class Home implements OnInit, OnDestroy {
|
|||
if (this.batchAction === 'delete') {
|
||||
for (const id of this.selectedIds) {
|
||||
await new Promise<void>((resolve) => {
|
||||
this.OedbApi.deleteEvent(id).subscribe({ next: () => { success++; resolve(); }, error: (err) => { (err?.status === 0 ? networkErrors++ : failed++); resolve(); } });
|
||||
this.OedbApi.deleteEvent(id).subscribe({
|
||||
next: () => {
|
||||
success++;
|
||||
resolve();
|
||||
}, error: (err) => {
|
||||
(err?.status === 0 ? networkErrors++ : failed++);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else if (this.batchAction === 'changeWhat') {
|
||||
const what = this.batchWhat.trim();
|
||||
if (!what) return;
|
||||
for (const id of this.selectedIds) {
|
||||
await doUpdate(id, (feature: any) => ({ ...feature, properties: { ...feature.properties, what } }));
|
||||
await doUpdate(id, (feature: any) => ({...feature, properties: {...feature.properties, what}}));
|
||||
}
|
||||
} else if (this.batchAction === 'setField') {
|
||||
const key = this.batchFieldKey.trim();
|
||||
if (!key) return;
|
||||
for (const id of this.selectedIds) {
|
||||
await doUpdate(id, (feature: any) => ({ ...feature, properties: { ...feature.properties, [key]: this.batchFieldValue } }));
|
||||
await doUpdate(id, (feature: any) => ({
|
||||
...feature,
|
||||
properties: {...feature.properties, [key]: this.batchFieldValue}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
this.batchSummary = { success, failed, networkErrors };
|
||||
this.batchSummary = {success, failed, networkErrors};
|
||||
this.loadEvents();
|
||||
}
|
||||
|
||||
|
@ -569,21 +613,25 @@ export class Home implements OnInit, OnDestroy {
|
|||
description: '',
|
||||
what: 'traffic.mammoth',
|
||||
where: '',
|
||||
...(osmUsername && { last_modified_by: osmUsername })
|
||||
...(osmUsername && {last_modified_by: osmUsername})
|
||||
},
|
||||
geometry: { type: 'Point', coordinates: [0, 0] }
|
||||
geometry: {type: 'Point', coordinates: [0, 0]}
|
||||
};
|
||||
this.showEditForm = true;
|
||||
}
|
||||
|
||||
private buildSubthemes() {
|
||||
const t = this.theme();
|
||||
if (!t) { this.subthemes = []; this.activeSubtheme.set(null); return; }
|
||||
if (!t) {
|
||||
this.subthemes = [];
|
||||
this.activeSubtheme.set(null);
|
||||
return;
|
||||
}
|
||||
const what = oedb.presets.what as Record<string, any>;
|
||||
const list: Array<{ key: string, label: string, emoji: string }> = [];
|
||||
Object.keys(what).forEach(k => {
|
||||
if (k === t || k.startsWith(`${t}.`)) {
|
||||
list.push({ key: k, label: what[k].label || k, emoji: what[k].emoji || '' });
|
||||
list.push({key: k, label: what[k].label || k, emoji: what[k].emoji || ''});
|
||||
}
|
||||
});
|
||||
this.subthemes = list.sort((a, b) => a.key.localeCompare(b.key));
|
||||
|
@ -592,7 +640,10 @@ export class Home implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
downloadGeoJSON() {
|
||||
const blob = new Blob([JSON.stringify({ type: 'FeatureCollection', features: this.filteredFeatures }, null, 2)], { type: 'application/geo+json' });
|
||||
const blob = new Blob([JSON.stringify({
|
||||
type: 'FeatureCollection',
|
||||
features: this.filteredFeatures
|
||||
}, null, 2)], {type: 'application/geo+json'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
|
@ -615,7 +666,7 @@ export class Home implements OnInit, OnDestroy {
|
|||
JSON.stringify(f?.geometry?.coordinates?.[1] ?? '')
|
||||
].join(','));
|
||||
const csv = [header.join(','), ...rows].join('\n');
|
||||
const blob = new Blob([csv], { type: 'text/csv' });
|
||||
const blob = new Blob([csv], {type: 'text/csv'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
|
@ -631,7 +682,7 @@ export class Home implements OnInit, OnDestroy {
|
|||
const end = (this.endDateStr || '').trim() || undefined;
|
||||
const days = this.daysAhead;
|
||||
const what = (this.selectedWhatFilter || '').trim() || undefined;
|
||||
this.loadEvents({ start, end, daysAhead: days, what });
|
||||
this.loadEvents({start, end, daysAhead: days, what});
|
||||
}
|
||||
|
||||
onBboxFilterToggle() {
|
||||
|
@ -657,12 +708,13 @@ export class Home implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
// Méthode pour recharger les événements quand la carte bouge
|
||||
showGuidePresetPlace: boolean = false;
|
||||
showGuidePresetMoreInfo: boolean= false;
|
||||
showGuidePresetPlace: boolean = false;
|
||||
showGuidePresetMoreInfo: boolean = false;
|
||||
|
||||
guidePresetMoreInfoPseudo: string = '';
|
||||
presetMoreDetails: string = '';
|
||||
guidePresetMoreInfoPseudo: string = '';
|
||||
presetMoreDetails: string = '';
|
||||
selectedPreset: string = '';
|
||||
|
||||
onMapMove(bbox: { minLng: number, minLat: number, maxLng: number, maxLat: number }) {
|
||||
this.setCurrentBbox(bbox);
|
||||
}
|
||||
|
|
|
@ -131,6 +131,13 @@ label { font-size: 0.85rem; color: $color-muted; }
|
|||
|
||||
.search{
|
||||
width: 20%;
|
||||
.btn{
|
||||
padding: 1rem 1.25rem;
|
||||
cursor: pointer;
|
||||
height: 2.7rem;
|
||||
line-height: 0.7rem;
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
}
|
||||
|
||||
.aside{
|
||||
|
@ -150,8 +157,8 @@ label { font-size: 0.85rem; color: $color-muted; }
|
|||
}
|
||||
}
|
||||
.actions{
|
||||
|
||||
position: fixed;
|
||||
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
left: 415px;
|
||||
right: 10px;
|
||||
|
@ -182,7 +189,7 @@ pre{
|
|||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
nav{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -226,7 +233,7 @@ nav{
|
|||
.loading-indicator{
|
||||
color: #007bff;
|
||||
font-size: 12px;
|
||||
position: fixed;
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 1000;
|
||||
|
@ -245,4 +252,30 @@ nav{
|
|||
&:active{
|
||||
background: linear-gradient( 135deg, #5982b1, #6992c1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main{
|
||||
nav{
|
||||
background: $color-text ;
|
||||
a {
|
||||
|
||||
color: white;
|
||||
&:hover{
|
||||
background: white;
|
||||
color: $color-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
main{
|
||||
height: calc(100vh - 0.1rem);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-attrib{
|
||||
position: fixed;
|
||||
bottom: 0 ;
|
||||
right: 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue