oedb-backend/frontend/src/app/pages/home/home.html
2025-10-14 17:29:37 +02:00

341 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div class="layout">
@if(showOptions){
<div class="aside">
<div class="aside-content">
<div class="toolbar">
<!-- <span class="loading-indicator">⏳</span> -->
@if (isLoading) {
<span class="loading-indicator"></span>
}
</div>
<div class="filters">
<label (click)="showFilters = !showFilters">
Filtre rapide
@if (showFilters) {
<span></span>
} @else {
<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) {
<span class="muted">{{filteredFeatures.length}} évènements chargés</span>
<hr>
<div class="controls">
<div class="control-group">
<label>Jours à venir</label>
<input
type="number"
class="input"
[(ngModel)]="daysAhead"
(ngModelChange)="onDaysAheadChange()"
min="1"
max="30"
placeholder="7">
</div>
<div class="control-group">
<label>
<input
type="checkbox"
[(ngModel)]="autoReloadEnabled"
(change)="toggleAutoReload()">
Rechargement auto (1min)
</label>
</div>
<div class="control-group">
<label>
<input
type="checkbox"
[(ngModel)]="useBboxFilter"
(change)="onBboxFilterToggle()">
Filtrer par zone visible (bbox)
</label>
</div>
</div>
<input class="input" type="text" placeholder="Rechercher..." [(ngModel)]="searchText" (ngModelChange)="onSearchChange()">
<div class="control-group">
<label>Période (début / fin)</label>
<div style="display:flex; gap:6px;">
<input class="input" type="date" [(ngModel)]="startDateStr">
<input class="input" type="date" [(ngModel)]="endDateStr">
</div>
</div>
<div class="control-group">
<button class="btn" (click)="onQuickSearchSubmit()">Rechercher</button>
</div>
<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-menu></app-menu> -->
<hr>
}
</div>
</div>
<!-- <app-unlocated-events [events]="filteredFeatures"></app-unlocated-events> -->
<hr>
@if(showEditForm){
<div class="guide">
<h3>Guide</h3>
<ul>
<li> Créer un évènement: Cliquez sur le bouton "+" pour créer un nouvel évènement. Sélectionnez un preset, remplissez les informations, cliquez quelque part sur la carte pour définir un emplacement. Puis appuyez sur créer.</li>
<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">
<button class="button btn btn-primary" (click)="createEvent()" title="Créer un évènement">+ nouvel évènement</button>
<button class="button" (click)="toggleView()" title="Basculer carte / tableau">📊</button>
<div class="downloaders">
<button class="button" (click)="downloadGeoJSON()" title="Télécharger GeoJSON">📥 GeoJSON</button>
<button class="button" (click)="downloadCSV()" title="Télécharger CSV">📥 CSV</button>
</div>
<div class="selectors">
<button class="button" [class.active]="selectionMode==='rectangle'" (click)="startRectSelection()" title="Sélection rectangulaire"></button>
<button class="button" [class.active]="selectionMode==='polygon'" (click)="startPolySelection()" title="Sélection polygone"></button>
@if (selectedIds.length) {
<span class="muted">{{selectedIds.length}} sélectionné(s)</span>
}
</div>
</div>
@if(selected !== null){
<div class="selected">
<h3> sélectionné: {{selected.properties.name}}</h3>
{{selected.properties.label}}
<br>
{{selected.properties.label}}
<br>
{{selected.properties.what}}
<br>
{{selected.properties.where}}
<br>
{{selected.properties.lat}}
<br>
{{selected.properties.lon}}
<br>
{{selected.properties.wikidata}}
<br>
{{selected.properties.featureType}}
<br>
{{selected.properties.type}}
<br>
start:
{{selected.properties.start}}
<br>
end:
{{selected.properties.stop}}
<br>
source
{{selected.properties.source}}
<br>
description:
{{selected.properties.description}}
<br>
createdate:
{{selected.properties.createdate}}
<br>
lastupdate:
{{selected.properties.lastupdate}}
</div>
}
</div>
</div>
}
<div class="main {{showOptions? 'is-small' : 'is-full'}}">
<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>
<div class="chips">
@for (t of subthemes; track t.key) {
<button class="chip" [class.active]="activeSubtheme()===t.key" (click)="activeSubtheme.set(t.key)">
<span class="emoji">{{t.emoji}}</span>
<span>{{t.label}}</span>
</button>
}
</div>
</div>
}
@if (!showTable && !showUnlocatedList) {
<div class="map">
<app-all-events [features]="filteredFeatures" [selected]="selected" [selectMode]="selectionMode" (selection)="onSelection($event)" (select)="onSelect($event)" (pickCoords)="onPickCoords($event)"></app-all-events>
</div>
} @else if (showUnlocatedList) {
<div class="table-wrapper" style="overflow:auto;height:100%;">
<div class="unlocated-layout">
<aside class="agenda-sidebar">
<div class="sidebar-header">
<h3>Sans lieu / en ligne</h3>
<small>{{unlocatedOrOnline.length}} évènements</small>
</div>
<div class="day-groups">
<ul class="event-list">
@for (f of unlocatedOrOnline; track f.id) {
<li class="event-item" (click)="onSelect({ id: f?.properties?.id ?? f?.id, properties: f.properties, geometry: f.geometry })" [class.active]="selected?.id === (f?.properties?.id ?? f?.id)">
<span class="event-icon">📌</span>
<div class="event-meta">
<div class="event-title">{{f?.properties?.label || f?.properties?.name || 'Événement'}}</div>
<div class="event-when">{{f?.properties?.start || f?.properties?.when || '—'}}</div>
</div>
</li>
}
</ul>
</div>
</aside>
<main class="agenda-main">
@if (selected) {
<div class="event-edit-panel">
<div class="panel-header">
<h3>Détails</h3>
<button class="btn-close" (click)="selected = null">×</button>
</div>
<div class="panel-content">
<app-edit-form
[selected]="selected"
(saved)="onSaved($event)"
(created)="onCreated($event)"
(deleted)="onDeleted($event)">
</app-edit-form>
</div>
</div>
} @else {
<div class="hint">Sélectionnez un évènement à gauche pour voir les détails.</div>
}
</main>
</div>
</div>
} @else {
<div class="table-wrapper" style="overflow:auto;height:100%;">
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr>
<th style="text-align:left;padding:6px;border-bottom:1px solid #e5e7eb;">Type</th>
<th style="text-align:left;padding:6px;border-bottom:1px solid #e5e7eb;">Label</th>
<th style="text-align:left;padding:6px;border-bottom:1px solid #e5e7eb;">Start</th>
<th style="text-align:left;padding:6px;border-bottom:1px solid #e5e7eb;">Stop</th>
</tr>
</thead>
<tbody>
@for (f of filteredFeatures; track f.id) {
<tr (click)="onSelect({ id: f?.properties?.id ?? f?.id, properties: f.properties, geometry: f.geometry })" style="cursor:pointer;">
<td style="padding:6px;border-bottom:1px solid #f1f5f9;">{{f?.properties?.what}}</td>
<td style="padding:6px;border-bottom:1px solid #f1f5f9;">{{f?.properties?.label || f?.properties?.name}}</td>
<td style="padding:6px;border-bottom:1px solid #f1f5f9;">{{f?.properties?.start || f?.properties?.when}}</td>
<td style="padding:6px;border-bottom:1px solid #f1f5f9;">{{f?.properties?.stop}}</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</div>
@if (selectedIds.length) {
<div class="batch-panel">
<div class="panel">
<div class="row">
<label>Action de masse</label>
<select class="input" [(ngModel)]="batchAction">
<option value="none">Choisir...</option>
<option value="changeWhat">Changer le type d'évènement (what)</option>
<option value="setField">Remplacer une propriété</option>
<option value="delete">Supprimer</option>
</select>
</div>
@if (batchAction==='changeWhat') {
<div class="row">
<label>Nouveau "what"</label>
<input class="input" type="text" [(ngModel)]="batchWhat" placeholder="ex: traffic.roadwork" />
</div>
}
@if (batchAction==='setField') {
<div class="row">
<label>Clé de propriété</label>
<input class="input" type="text" [(ngModel)]="batchFieldKey" placeholder="ex: where" />
</div>
<div class="row">
<label>Nouvelle valeur</label>
<input class="input" type="text" [(ngModel)]="batchFieldValue" placeholder="ex: Paris, 12e" />
</div>
}
<div class="actions">
<button class="btn" (click)="applyBatch()" [disabled]="batchAction==='none'">Appliquer</button>
<button class="btn btn-ghost" (click)="clearSelection()">Annuler</button>
</div>
@if (batchSummary) {
<div class="summary">
<span>Succès: {{batchSummary.success}}</span>
<span>Échecs: {{batchSummary.failed}}</span>
<span>Erreurs réseau: {{batchSummary.networkErrors}}</span>
</div>
}
</div>
</div>
}
<!-- Boutons flottants en bas à droite -->
<div class="floating-actions">
<button class="fab counter" (click)="toggleUnlocatedPanel()" title="Non localisés ou en ligne">
{{unlocatedOrOnline.length}}
</button>
<button class="fab plus" (click)="createMammoth()" title="Créer un nouvel évènement (mammouth)">+
</button>
</div>