import { Component, inject, OnInit, ViewChild, TemplateRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { OedbApi } from '../../services/oedb-api'; import { EditForm } from '../../forms/edit-form/edit-form'; import { CalendarModule, CalendarView, CalendarEvent } from 'angular-calendar'; import { CalendarEventAction, CalendarEventTimesChangedEvent } from 'angular-calendar'; import oedb from '../../../oedb-types'; interface OedbEvent { id: string; properties: { label?: string; name?: string; what?: string; start?: string; when?: string; stop?: string; description?: string; where?: string; }; geometry?: { type: string; coordinates: [number, number]; }; } interface DayEvents { date: Date; events: OedbEvent[]; } @Component({ selector: 'app-agenda', standalone: true, imports: [CommonModule, FormsModule, EditForm, CalendarModule], templateUrl: './agenda.html', styleUrl: './agenda.scss' }) export class Agenda implements OnInit { private oedbApi = inject(OedbApi); @ViewChild('eventTitleTemplate', { static: true }) eventTitleTemplate!: TemplateRef; events: OedbEvent[] = []; filteredEvents: OedbEvent[] = []; calendarEvents: CalendarEvent[] = []; selectedEvent: OedbEvent | null = null; showSidePanel = false; showFiltersPanel = false; view: CalendarView = CalendarView.Month; viewDate: Date = new Date(); oedbPresets = oedb.presets.what; // Propriétés pour les filtres hideTrafficEvents = true; // Par défaut, masquer les événements de type traffic selectedEventTypes: string[] = []; availableEventTypes: string[] = []; // Exposer CalendarView pour l'utiliser dans le template CalendarView = CalendarView; ngOnInit() { this.loadEvents(); } loadEvents() { const today = new Date(); const startDate = new Date(today); startDate.setDate(today.getDate() - 10); const endDate = new Date(today); endDate.setDate(today.getDate() + 10); const params = { start: `${startDate.toISOString().split('T')[0]}`, end: `${endDate.toISOString().split('T')[0]}`, limit: 1000 }; this.oedbApi.getEvents(params).subscribe((response: any) => { this.events = Array.isArray(response?.features) ? response.features : []; this.updateAvailableEventTypes(); this.applyFilters(); }); } updateAvailableEventTypes() { const eventTypes = new Set(); this.events.forEach(event => { if (event?.properties?.what) { eventTypes.add(event.properties.what); } }); this.availableEventTypes = Array.from(eventTypes).sort(); } applyFilters() { let filtered = [...this.events]; // Filtre par défaut : masquer les événements de type traffic if (this.hideTrafficEvents) { filtered = filtered.filter(event => !event?.properties?.what?.startsWith('traffic.') ); } // Filtre par types d'événements sélectionnés if (this.selectedEventTypes.length > 0) { filtered = filtered.filter(event => this.selectedEventTypes.includes(event?.properties?.what || '') ); } this.filteredEvents = filtered; this.organizeEventsByDay(); } organizeEventsByDay() { this.calendarEvents = this.filteredEvents.map(event => { const eventDate = this.getEventDate(event); const preset = this.getEventPreset(event); return { id: event.id, title: this.getEventTitle(event), start: eventDate || new Date(), color: this.getEventColor(preset), meta: { event: event, preset: preset } }; }); } getEventDate(event: OedbEvent): Date | null { const startDate = event.properties.start || event.properties.when; if (startDate) { return new Date(startDate); } return null; } getEventTitle(event: OedbEvent): string { return event.properties.label || event.properties.name || 'Événement sans titre'; } getEventTime(event: OedbEvent): string { const startDate = event.properties.start || event.properties.when; if (startDate) { const date = new Date(startDate); return date.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }); } return ''; } selectEvent(event: OedbEvent) { this.selectedEvent = event; this.showSidePanel = true; } onEventClicked({ event }: { event: CalendarEvent; sourceEvent: MouseEvent | KeyboardEvent }) { if (event.meta && event.meta.event) { this.selectEvent(event.meta.event); } } getEventPreset(event: OedbEvent): any { const what = event.properties.what; if (what && (this.oedbPresets as any)[what]) { return (this.oedbPresets as any)[what]; } return null; } getEventIcon(preset: any): string { if (preset) { return preset.emoji || '📅'; } return '📅'; } getEventColor(preset: any): any { if (preset) { // Couleurs basées sur la catégorie const categoryColors: { [key: string]: any } = { 'Communauté': { primary: '#007bff', secondary: '#cce7ff' }, 'Culture': { primary: '#28a745', secondary: '#d4edda' }, 'Musique': { primary: '#ffc107', secondary: '#fff3cd' }, 'Énergie': { primary: '#dc3545', secondary: '#f8d7da' }, 'Commerce': { primary: '#6f42c1', secondary: '#e2d9f3' }, 'Temps': { primary: '#17a2b8', secondary: '#d1ecf1' }, 'Tourisme': { primary: '#fd7e14', secondary: '#ffeaa7' }, 'Circulation': { primary: '#6c757d', secondary: '#e9ecef' }, 'Randonnée': { primary: '#20c997', secondary: '#d1f2eb' }, 'Vie sauvage': { primary: '#795548', secondary: '#efebe9' }, 'Météo': { primary: '#2196f3', secondary: '#e3f2fd' } }; const category = preset.category || 'Communauté'; return categoryColors[category] || { primary: '#6c757d', secondary: '#e9ecef' }; } return { primary: '#6c757d', secondary: '#e9ecef' }; } closeSidePanel() { this.showSidePanel = false; this.selectedEvent = null; } onEventSaved(event: any) { this.loadEvents(); // Recharger les événements après modification this.closeSidePanel(); } onEventCreated(event: any) { this.loadEvents(); // Recharger les événements après création this.closeSidePanel(); } onEventDeleted(event: any) { this.loadEvents(); // Recharger les événements après suppression this.closeSidePanel(); } setView(view: CalendarView) { this.view = view; } dayClicked(event: any): void { // Gérer les différents types d'événements selon la vue let date: Date; let events: CalendarEvent[] = []; if (event.day) { // Vue mois : { day: MonthViewDay, sourceEvent: MouseEvent | KeyboardEvent } date = event.day.date; events = event.day.events || []; } else if (event.date) { // Vue semaine/jour : { date: Date, events: CalendarEvent[], sourceEvent: MouseEvent | KeyboardEvent } date = event.date; events = event.events || []; } else { // Fallback pour les autres cas console.warn('Type d\'événement dayClicked non reconnu:', event); return; } console.log('Day clicked:', date, events); } eventTimesChanged({ event, newStart, newEnd, }: CalendarEventTimesChangedEvent): void { console.log('Event times changed:', event, newStart, newEnd); } toggleFiltersPanel() { this.showFiltersPanel = !this.showFiltersPanel; } onHideTrafficChange() { this.applyFilters(); } onEventTypeChange(eventType: string, checked: boolean) { if (checked) { if (!this.selectedEventTypes.includes(eventType)) { this.selectedEventTypes.push(eventType); } } else { this.selectedEventTypes = this.selectedEventTypes.filter(type => type !== eventType); } this.applyFilters(); } isEventTypeSelected(eventType: string): boolean { return this.selectedEventTypes.includes(eventType); } clearAllFilters() { this.selectedEventTypes = []; this.hideTrafficEvents = true; this.applyFilters(); } }