296 lines
9.1 KiB
TypeScript
296 lines
9.1 KiB
TypeScript
import { Component, OnInit, signal } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { MeteocatService } from '../../services/meteocat.service';
|
|
import { Municipi } from '../../models/municipi.model';
|
|
import { PrediccioDiaria } from '../../models/prediccio-diaria.model';
|
|
import { PrediccioHoraria } from '../../models/prediccio-horaria.model';
|
|
import { EstacioMesCercanaResponse } from '../../models/estacio-mes-cercana.model';
|
|
import { PrediccioDiariaComponent } from '../prediccio-diaria/prediccio-diaria';
|
|
import { PrediccioHorariaComponent } from '../prediccio-horaria/prediccio-horaria';
|
|
|
|
@Component({
|
|
selector: 'app-home',
|
|
imports: [CommonModule, PrediccioDiariaComponent, PrediccioHorariaComponent],
|
|
templateUrl: './home.html',
|
|
styleUrl: './home.scss',
|
|
})
|
|
export class Home implements OnInit {
|
|
// Municipio dinámico según GPS
|
|
municipiCodi = signal<string>('081075'); // Lliçà d'Amunt por defecto
|
|
|
|
municipi = signal<Municipi | null>(null);
|
|
prediccioDiaria = signal<PrediccioDiaria[]>([]);
|
|
prediccioHoraria = signal<PrediccioHoraria[]>([]);
|
|
estacioInfo = signal<EstacioMesCercanaResponse | null>(null);
|
|
|
|
loading = signal(true);
|
|
error = signal<string | null>(null);
|
|
gettingLocation = signal(false);
|
|
locationDenied = signal(false);
|
|
|
|
// Menú hamburguesa
|
|
menuOpen = signal(false);
|
|
searchQuery = signal('');
|
|
searchResults = signal<Municipi[]>([]);
|
|
searching = signal(false);
|
|
|
|
// Modal GPS
|
|
showGpsModal = signal(false);
|
|
gpsPermissionAsked = signal(false);
|
|
|
|
constructor(private meteocatService: MeteocatService) {}
|
|
|
|
ngOnInit(): void {
|
|
// Verificar si ya se pidió permiso GPS antes
|
|
const gpsRejected = localStorage.getItem('gpsRejected');
|
|
const gpsAsked = localStorage.getItem('gpsAsked');
|
|
|
|
if (gpsRejected === 'true') {
|
|
// Usuario rechazó GPS previamente, cargar datos por defecto
|
|
this.gpsPermissionAsked.set(true);
|
|
this.loadWeatherData(this.municipiCodi());
|
|
} else if (gpsAsked === 'true') {
|
|
// Ya se pidió permiso antes, intentar obtener ubicación automáticamente
|
|
this.gpsPermissionAsked.set(true);
|
|
this.getCurrentLocation();
|
|
} else {
|
|
// Primera vez, mostrar modal
|
|
this.showGpsModal.set(true);
|
|
this.loadWeatherData(this.municipiCodi()); // Cargar datos por defecto mientras
|
|
}
|
|
}
|
|
|
|
getCurrentLocation(): void {
|
|
if (!navigator.geolocation) {
|
|
console.warn('Geolocalización no disponible, usando municipio por defecto');
|
|
this.loadWeatherData(this.municipiCodi());
|
|
return;
|
|
}
|
|
|
|
this.gettingLocation.set(true);
|
|
this.loading.set(true);
|
|
|
|
navigator.geolocation.getCurrentPosition(
|
|
(position) => {
|
|
const lat = position.coords.latitude;
|
|
const lon = position.coords.longitude;
|
|
|
|
console.log(`Ubicación GPS obtenida: ${lat}, ${lon}`);
|
|
|
|
// Marcar que el usuario aceptó GPS
|
|
localStorage.setItem('gpsAsked', 'true');
|
|
localStorage.removeItem('gpsRejected');
|
|
this.locationDenied.set(false);
|
|
|
|
// Buscar municipio más cercano
|
|
this.meteocatService.getMunicipiCercano(lat, lon).subscribe({
|
|
next: (municipi) => {
|
|
console.log(`Municipio más cercano: ${municipi.nom} (${municipi.codi})`);
|
|
this.municipiCodi.set(municipi.codi);
|
|
this.gettingLocation.set(false);
|
|
this.loadWeatherData(municipi.codi);
|
|
},
|
|
error: (err) => {
|
|
console.error('Error al buscar municipio cercano:', err);
|
|
this.gettingLocation.set(false);
|
|
// Usar municipio por defecto si falla
|
|
this.loadWeatherData(this.municipiCodi());
|
|
}
|
|
});
|
|
},
|
|
(error) => {
|
|
console.warn('Error obteniendo ubicación:', error.message);
|
|
this.gettingLocation.set(false);
|
|
|
|
// Solo mostrar banner si el usuario lo solicitó manualmente
|
|
if (!this.gpsPermissionAsked()) {
|
|
this.locationDenied.set(true);
|
|
}
|
|
|
|
// Usar municipio por defecto si se deniega el permiso
|
|
this.loadWeatherData(this.municipiCodi());
|
|
},
|
|
{
|
|
enableHighAccuracy: true,
|
|
timeout: 10000,
|
|
maximumAge: 300000 // Cache de 5 minutos
|
|
}
|
|
);
|
|
}
|
|
|
|
loadWeatherData(codiMunicipi: string): void {
|
|
this.loading.set(true);
|
|
this.error.set(null);
|
|
|
|
// Cargar municipio
|
|
this.meteocatService.getMunicipiByCode(codiMunicipi).subscribe({
|
|
next: (municipi) => {
|
|
this.municipi.set(municipi);
|
|
},
|
|
error: (err) => {
|
|
console.error('Error al cargar municipio:', err);
|
|
this.error.set('Error al cargar información del municipio');
|
|
}
|
|
});
|
|
|
|
// Cargar predicción diaria
|
|
this.meteocatService.getPrediccioDiaria(codiMunicipi).subscribe({
|
|
next: (prediccions) => {
|
|
this.prediccioDiaria.set(prediccions);
|
|
},
|
|
error: (err) => {
|
|
console.error('Error al cargar predicción diaria:', err);
|
|
this.error.set('Error al cargar predicción diaria');
|
|
}
|
|
});
|
|
|
|
// Cargar predicción horaria
|
|
this.meteocatService.getPrediccioHoraria(codiMunicipi).subscribe({
|
|
next: (prediccions) => {
|
|
// Filtrar solo predicciones desde la hora actual y limitamos a 48 horas
|
|
const now = new Date();
|
|
const filtered = prediccions
|
|
.filter(p => {
|
|
const predDate = new Date(p.dataHora);
|
|
return predDate >= now;
|
|
})
|
|
.slice(0, 48); // Limitar a 48 horas
|
|
|
|
this.prediccioHoraria.set(filtered);
|
|
this.loading.set(false);
|
|
},
|
|
error: (err) => {
|
|
console.error('Error al cargar predicción horaria:', err);
|
|
this.error.set('Error al cargar predicción horaria');
|
|
this.loading.set(false);
|
|
}
|
|
});
|
|
|
|
// Cargar estación XEMA más cercana
|
|
this.meteocatService.getEstacioCercana(codiMunicipi).subscribe({
|
|
next: (response) => {
|
|
this.estacioInfo.set(response);
|
|
},
|
|
error: (err) => {
|
|
console.error('Error al cargar estación XEMA:', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
getCurrentTemperature(): number | null {
|
|
const horariaData = this.prediccioHoraria();
|
|
if (horariaData && horariaData.length > 0) {
|
|
return horariaData[0].temperatura;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
getTodayMinMax(): { min: number; max: number } | null {
|
|
const diariaData = this.prediccioDiaria();
|
|
if (diariaData && diariaData.length > 0) {
|
|
return {
|
|
min: diariaData[0].temperaturaMinima,
|
|
max: diariaData[0].temperaturaMaxima
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
refresh(): void {
|
|
// Recargar datos con el municipio actual
|
|
this.loadWeatherData(this.municipiCodi());
|
|
}
|
|
|
|
requestLocation(): void {
|
|
// Solicitar ubicación nuevamente
|
|
this.locationDenied.set(false);
|
|
this.getCurrentLocation();
|
|
}
|
|
|
|
acceptGpsModal(): void {
|
|
this.showGpsModal.set(false);
|
|
this.gpsPermissionAsked.set(true);
|
|
localStorage.setItem('gpsAsked', 'true');
|
|
this.getCurrentLocation();
|
|
}
|
|
|
|
rejectGpsModal(): void {
|
|
this.showGpsModal.set(false);
|
|
this.gpsPermissionAsked.set(true);
|
|
localStorage.setItem('gpsRejected', 'true');
|
|
localStorage.setItem('gpsAsked', 'true');
|
|
// Ya tenemos los datos cargados por defecto
|
|
}
|
|
|
|
toggleMenu(): void {
|
|
this.menuOpen.set(!this.menuOpen());
|
|
}
|
|
|
|
closeMenu(): void {
|
|
this.menuOpen.set(false);
|
|
this.searchQuery.set('');
|
|
this.searchResults.set([]);
|
|
}
|
|
|
|
onSearchInput(event: Event): void {
|
|
const input = event.target as HTMLInputElement;
|
|
const query = input.value.trim();
|
|
this.searchQuery.set(query);
|
|
|
|
if (query.length < 2) {
|
|
this.searchResults.set([]);
|
|
return;
|
|
}
|
|
|
|
this.searching.set(true);
|
|
this.meteocatService.searchMunicipis(query).subscribe({
|
|
next: (results) => {
|
|
this.searchResults.set(results);
|
|
this.searching.set(false);
|
|
},
|
|
error: (err) => {
|
|
console.error('Error searching municipis:', err);
|
|
this.searching.set(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
selectMunicipi(municipi: Municipi): void {
|
|
console.log(`Seleccionado municipio: ${municipi.nom} (${municipi.codi})`);
|
|
this.municipiCodi.set(municipi.codi);
|
|
this.closeMenu();
|
|
this.loadWeatherData(municipi.codi);
|
|
}
|
|
|
|
getLastUpdateDate(): string | null {
|
|
const horaria = this.prediccioHoraria();
|
|
if (!horaria || horaria.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const lastDate = new Date(horaria[0].dataHora);
|
|
const now = new Date();
|
|
const diffMinutes = Math.floor((now.getTime() - lastDate.getTime()) / 60000);
|
|
|
|
if (diffMinutes < 60) {
|
|
return `fa ${diffMinutes} minuts`;
|
|
} else if (diffMinutes < 1440) {
|
|
const hours = Math.floor(diffMinutes / 60);
|
|
return `fa ${hours} ${hours === 1 ? 'hora' : 'hores'}`;
|
|
} else {
|
|
const options: Intl.DateTimeFormatOptions = {
|
|
day: 'numeric',
|
|
month: 'short',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
};
|
|
return lastDate.toLocaleDateString('ca-ES', options);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error calculating last update date:', error);
|
|
return null;
|
|
}
|
|
}
|
|
}
|