oedb-backend/frontend/src/app/pages/home/home.ts

341 lines
9.6 KiB
TypeScript
Raw Normal View History

2025-10-07 14:42:39 +02:00
import { Component, inject, signal , OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { FormsModule } from '@angular/forms';
2025-10-02 23:19:15 +02:00
import {Menu} from './menu/menu';
2025-10-03 13:40:08 +02:00
import { AllEvents } from '../../maps/all-events/all-events';
import { EditForm } from '../../forms/edit-form/edit-form';
2025-10-03 11:56:55 +02:00
import { OedbApi } from '../../services/oedb-api';
import { ActivatedRoute } from '@angular/router';
import oedb from '../../../oedb-types';
2025-10-04 12:46:25 +02:00
import { UnlocatedEvents } from '../../shared/unlocated-events/unlocated-events';
import { OsmAuth } from '../../services/osm-auth';
import { Osm } from '../../forms/osm/osm';
2025-10-05 00:24:43 +02:00
import { WhatFilterComponent } from '../../shared/what-filter/what-filter';
2025-10-02 22:53:50 +02:00
@Component({
selector: 'app-home',
2025-10-04 12:58:44 +02:00
standalone: true,
2025-10-02 23:19:15 +02:00
imports: [
2025-10-03 13:40:08 +02:00
Menu,
AllEvents,
2025-10-04 12:46:25 +02:00
UnlocatedEvents,
EditForm,
Osm,
2025-10-05 00:24:43 +02:00
FormsModule,
WhatFilterComponent
2025-10-02 23:19:15 +02:00
],
2025-10-02 22:53:50 +02:00
templateUrl: './home.html',
styleUrl: './home.scss'
})
export class Home implements OnInit, OnDestroy {
2025-10-03 11:56:55 +02:00
OedbApi = inject(OedbApi);
route = inject(ActivatedRoute);
private router = inject(Router);
private osmAuth = inject(OsmAuth);
2025-10-03 13:40:08 +02:00
features: Array<any> = [];
filteredFeatures: Array<any> = [];
2025-10-03 13:40:08 +02:00
selected: any | null = null;
2025-10-03 14:00:35 +02:00
showTable = false;
2025-10-04 23:36:37 +02:00
showFilters = false;
2025-10-05 00:21:11 +02:00
showEditForm = true;
2025-10-10 10:14:30 +02:00
selectionMode: 'none' | 'rectangle' | 'polygon' = 'none';
selectedIds: Array<string | number> = [];
batchAction: 'none' | 'changeWhat' | 'delete' = 'none';
batchWhat = '';
// Nouvelles propriétés pour le rechargement automatique et la sélection de jours
autoReloadEnabled = true;
autoReloadInterval: any = null;
daysAhead = 7; // Nombre de jours dans le futur par défaut
isLoading = false;
// Propriétés pour les filtres
searchText = '';
selectedWhatFilter = '';
availableWhatTypes: string[] = [];
theme = signal<string | null>(null);
subthemes: Array<{ key: string, label: string, emoji: string }> = [];
activeSubtheme = signal<string | null>(null);
2025-10-03 11:56:55 +02:00
ngOnInit() {
this.loadEvents();
this.startAutoReload();
}
ngOnDestroy() {
this.stopAutoReload();
}
2025-10-05 00:21:11 +02:00
createEvent() {
this.selected = null;
//this.showTable = false;
//this.showFilters = true;
this.showEditForm = true;
2025-10-05 00:21:11 +02:00
}
loadEvents() {
this.isLoading = true;
const today = new Date();
const endDate = new Date(today);
endDate.setDate(today.getDate() + this.daysAhead);
const params = {
start: today.toISOString().split('T')[0],
end: endDate.toISOString().split('T')[0],
2025-10-10 10:14:30 +02:00
limit: 3000
};
this.OedbApi.getEvents(params).subscribe((events: any) => {
2025-10-03 13:40:08 +02:00
this.features = Array.isArray(events?.features) ? events.features : [];
this.updateAvailableWhatTypes();
this.applyFilters();
this.isLoading = false;
2025-10-03 13:40:08 +02:00
});
}
startAutoReload() {
if (this.autoReloadEnabled && !this.autoReloadInterval) {
this.autoReloadInterval = setInterval(() => {
this.loadEvents();
}, 60000); // 1 minute
}
}
stopAutoReload() {
if (this.autoReloadInterval) {
clearInterval(this.autoReloadInterval);
this.autoReloadInterval = null;
}
}
toggleAutoReload() {
this.autoReloadEnabled = !this.autoReloadEnabled;
if (this.autoReloadEnabled) {
this.startAutoReload();
} else {
this.stopAutoReload();
}
}
onDaysAheadChange() {
this.loadEvents();
}
updateAvailableWhatTypes() {
const whatTypes = new Set<string>();
this.features.forEach(feature => {
if (feature?.properties?.what) {
whatTypes.add(feature.properties.what);
}
});
this.route.queryParams.subscribe(p => {
const t = (p?.['theme'] || '').trim();
this.theme.set(t || null);
this.buildSubthemes();
});
this.availableWhatTypes = Array.from(whatTypes).sort();
}
onSearchChange() {
this.applyFilters();
}
onWhatFilterChange() {
this.applyFilters();
}
applyFilters() {
let filtered = [...this.features];
// Filtre par texte de recherche
if (this.searchText.trim()) {
const searchLower = this.searchText.toLowerCase();
filtered = filtered.filter(feature => {
const label = feature?.properties?.label || feature?.properties?.name || '';
const description = feature?.properties?.description || '';
const what = feature?.properties?.what || '';
return label.toLowerCase().includes(searchLower) ||
description.toLowerCase().includes(searchLower) ||
what.toLowerCase().includes(searchLower);
});
}
// Filtre par type d'événement
if (this.selectedWhatFilter) {
filtered = filtered.filter(feature =>
feature?.properties?.what === this.selectedWhatFilter
);
}
this.filteredFeatures = filtered;
}
goToNewCategories() {
this.router.navigate(['/nouvelles-categories']);
}
2025-10-03 13:40:08 +02:00
onSelect(feature: any) {
this.selected = feature;
}
onPickCoords(coords: [number, number]) {
const [lon, lat] = coords;
if (this.selected && this.selected.properties) {
this.selected = {
...this.selected,
geometry: { type: 'Point', coordinates: [lon, lat] }
};
} else {
const osmUsername = this.osmAuth.getUsername();
const whatKey = this.activeSubtheme();
let label = '';
let description = '';
if (whatKey) {
const preset = (oedb.presets.what as any)[whatKey];
if (preset) {
label = preset.label || '';
description = preset.description || '';
}
}
2025-10-03 13:40:08 +02:00
this.selected = {
id: null,
properties: {
label: '',
description: '',
what: whatKey || '',
where: '',
...(osmUsername && { last_modified_by: osmUsername })
},
2025-10-03 13:40:08 +02:00
geometry: { type: 'Point', coordinates: [lon, lat] }
};
}
}
onSaved(_res: any) {
// refresh list after update
this.loadEvents();
2025-10-03 13:40:08 +02:00
}
onCreated(_res: any) {
this.selected = null;
this.loadEvents();
2025-10-03 13:40:08 +02:00
}
onDeleted(_res: any) {
this.selected = null;
this.loadEvents();
2025-10-03 11:56:55 +02:00
}
2025-10-03 14:00:35 +02:00
2025-10-10 10:14:30 +02:00
// Selection from map
onSelection(ids: Array<string | number>) {
this.selectedIds = ids;
}
startRectSelection() {
this.selectionMode = this.selectionMode === 'rectangle' ? 'none' : 'rectangle';
}
startPolySelection() {
this.selectionMode = this.selectionMode === 'polygon' ? 'none' : 'polygon';
}
clearSelection() {
this.selectionMode = 'none';
this.selectedIds = [];
this.batchAction = 'none';
this.batchWhat = '';
}
async applyBatch() {
if (!this.selectedIds.length || this.batchAction === 'none') return;
if (this.batchAction === 'delete') {
for (const id of this.selectedIds) {
await new Promise<void>((resolve) => {
this.OedbApi.deleteEvent(id).subscribe({ next: () => resolve(), error: () => resolve() });
});
}
this.loadEvents();
this.clearSelection();
return;
}
if (this.batchAction === 'changeWhat') {
const what = this.batchWhat.trim();
if (!what) return;
for (const id of this.selectedIds) {
const feature = this.features.find(f => (f?.properties?.id ?? f?.id) === id);
if (!feature) continue;
const updated = { ...feature, properties: { ...feature.properties, what } };
await new Promise<void>((resolve) => {
this.OedbApi.updateEvent(id, updated).subscribe({ next: () => resolve(), error: () => resolve() });
});
}
this.loadEvents();
this.clearSelection();
}
}
2025-10-05 00:21:11 +02:00
onCanceled() {
this.showEditForm = false;
}
2025-10-03 14:00:35 +02:00
ngAfterViewInit() {
// reserved
2025-10-03 14:00:35 +02:00
}
toggleView() {
this.showTable = !this.showTable;
}
private buildSubthemes() {
const t = this.theme();
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 || '' });
}
});
this.subthemes = list.sort((a, b) => a.key.localeCompare(b.key));
const exact = this.subthemes.find(s => s.key === t);
this.activeSubtheme.set(exact ? exact.key : (this.subthemes[0]?.key || null));
}
2025-10-03 14:00:35 +02:00
downloadGeoJSON() {
const blob = new Blob([JSON.stringify({ type: 'FeatureCollection', features: this.filteredFeatures }, null, 2)], { type: 'application/geo+json' });
2025-10-03 14:00:35 +02:00
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'events.geojson';
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(url);
a.remove();
}
downloadCSV() {
const header = ['id', 'what', 'label', 'start', 'stop', 'lon', 'lat'];
const rows = this.filteredFeatures.map((f: any) => [
2025-10-03 14:00:35 +02:00
JSON.stringify(f?.properties?.id ?? f?.id ?? ''),
JSON.stringify(f?.properties?.what ?? ''),
JSON.stringify(f?.properties?.label ?? f?.properties?.name ?? ''),
JSON.stringify(f?.properties?.start ?? f?.properties?.when ?? ''),
JSON.stringify(f?.properties?.stop ?? ''),
JSON.stringify(f?.geometry?.coordinates?.[0] ?? ''),
JSON.stringify(f?.geometry?.coordinates?.[1] ?? '')
].join(','));
const csv = [header.join(','), ...rows].join('\n');
const blob = new Blob([csv], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'events.csv';
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(url);
a.remove();
}
2025-10-02 22:53:50 +02:00
}