mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-20 13:38:15 +02:00
Add weather information display and configuration options
Integrate weather data fetching, display in a widget, and provide a settings page for configuration. Replit-Commit-Author: Agent Replit-Commit-Session-Id: d43bd811-9372-45a7-8ac9-4a954c0538e1 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/d43bd811-9372-45a7-8ac9-4a954c0538e1/BIZe1t3
This commit is contained in:
1 parent
48bfc884ed
commit
e70e860097
9 files changed
+863
-4
No files matched your search
@@ -0,0 +1,172 @@
|
||||
import { WeatherData, WeatherSettings } from "../shared/schema.js";
|
||||
|
||||
export interface WeatherApiResponse {
|
||||
days: Array<{
|
||||
datetime: string;
|
||||
tempmax: number;
|
||||
tempmin: number;
|
||||
icon: string;
|
||||
conditions: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface WeatherDisplayData {
|
||||
currentYear: WeatherData | null;
|
||||
previousYear: WeatherData | null;
|
||||
}
|
||||
|
||||
export class WeatherService {
|
||||
private baseUrl = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline";
|
||||
|
||||
/**
|
||||
* Récupère les données météo actuelles depuis l'API Visual Crossing
|
||||
*/
|
||||
async fetchCurrentWeather(settings: WeatherSettings): Promise<WeatherApiResponse | null> {
|
||||
try {
|
||||
const url = `${this.baseUrl}/${encodeURIComponent(settings.location)}?unitGroup=metric&include=days&key=${settings.apiKey}&contentType=json`;
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
console.error(`Weather API error: ${response.status} ${response.statusText}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la récupération de la météo actuelle:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les données météo de l'année précédente pour la même date
|
||||
*/
|
||||
async fetchPreviousYearWeather(settings: WeatherSettings, targetDate: string): Promise<WeatherApiResponse | null> {
|
||||
try {
|
||||
const url = `${this.baseUrl}/${encodeURIComponent(settings.location)}?unitGroup=metric&include=days&key=${settings.apiKey}&contentType=json&forecastBasisDate=${targetDate}`;
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
console.error(`Weather API error for previous year: ${response.status} ${response.statusText}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la récupération de la météo de l'année précédente:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit les données de l'API en format WeatherData
|
||||
*/
|
||||
convertApiDataToWeatherData(apiData: WeatherApiResponse, location: string, isCurrentYear: boolean): WeatherData | null {
|
||||
if (!apiData.days || apiData.days.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const todayData = apiData.days[0];
|
||||
|
||||
return {
|
||||
id: 0, // Sera généré par la base de données
|
||||
date: todayData.datetime,
|
||||
location,
|
||||
tempMax: todayData.tempmax.toString(),
|
||||
tempMin: todayData.tempmin.toString(),
|
||||
icon: todayData.icon,
|
||||
conditions: todayData.conditions,
|
||||
isCurrentYear,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule la date de l'année précédente pour la même date
|
||||
*/
|
||||
getPreviousYearDate(date: Date = new Date()): string {
|
||||
const previousYear = new Date(date);
|
||||
previousYear.setFullYear(date.getFullYear() - 1);
|
||||
return previousYear.toISOString().split('T')[0]; // Format YYYY-MM-DD
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit l'icône météo en format d'affichage
|
||||
*/
|
||||
getWeatherIcon(iconCode: string): string {
|
||||
const iconMap: { [key: string]: string } = {
|
||||
'clear-day': '☀️',
|
||||
'clear-night': '🌙',
|
||||
'partly-cloudy-day': '⛅',
|
||||
'partly-cloudy-night': '☁️',
|
||||
'cloudy': '☁️',
|
||||
'rain': '🌧️',
|
||||
'snow': '❄️',
|
||||
'wind': '💨',
|
||||
'fog': '🌫️',
|
||||
'thunder': '⛈️',
|
||||
'hail': '🌨️'
|
||||
};
|
||||
|
||||
return iconMap[iconCode] || '🌤️';
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide les paramètres de configuration météo
|
||||
*/
|
||||
validateWeatherSettings(settings: Partial<WeatherSettings>): { isValid: boolean; errors: string[] } {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!settings.apiKey || settings.apiKey.trim().length === 0) {
|
||||
errors.push("La clé API Visual Crossing est requise");
|
||||
}
|
||||
|
||||
if (!settings.location || settings.location.trim().length === 0) {
|
||||
errors.push("La localisation est requise");
|
||||
}
|
||||
|
||||
// Vérification basique du format de la clé API Visual Crossing
|
||||
if (settings.apiKey && settings.apiKey.length < 10) {
|
||||
errors.push("La clé API semble invalide (trop courte)");
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: errors.length === 0,
|
||||
errors
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Test de connexion à l'API météo
|
||||
*/
|
||||
async testApiConnection(apiKey: string, location: string): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
const testUrl = `${this.baseUrl}/${encodeURIComponent(location)}?unitGroup=metric&include=days&key=${apiKey}&contentType=json`;
|
||||
|
||||
const response = await fetch(testUrl);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.days && data.days.length > 0) {
|
||||
return { success: true, message: "Connexion réussie à l'API météo" };
|
||||
} else {
|
||||
return { success: false, message: "Aucune donnée météo trouvée pour cette localisation" };
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
return { success: false, message: "Clé API invalide" };
|
||||
} else if (response.status === 404) {
|
||||
return { success: false, message: "Localisation introuvable" };
|
||||
} else {
|
||||
return { success: false, message: `Erreur API: ${response.status} ${response.statusText}` };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur test API météo:", error);
|
||||
return { success: false, message: "Erreur de connexion à l'API météo" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const weatherService = new WeatherService();
|
||||
Reference in new issue
Block a user