Add ability to detect user's city using geolocation

Implement geolocation functionality in the weather widget to automatically detect and set the user's city based on GPS coordinates, updating weather data accordingly.

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/zoZabWF
This commit is contained in:
michaelschal committed 2025-08-15 11:20:09 +00:00
1 parent 8eb0458ce2
commit c65860cb85
4 files changed
+202 -6

No files matched your search

+107 -5
View File
@@ -1,6 +1,9 @@
import { useQuery } from "@tanstack/react-query";
import { Cloud, CloudRain, Sun, CloudSnow, CloudSun, ThermometerSun, CloudDrizzle, Wind, Zap, Eye } from "lucide-react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Cloud, CloudRain, Sun, CloudSnow, CloudSun, ThermometerSun, CloudDrizzle, Wind, Zap, Eye, MapPin, Loader2 } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient";
interface WeatherData {
id: number;
@@ -118,12 +121,95 @@ const WeatherIcon = ({ condition, size = 20 }: { condition: string; size?: numbe
};
export default function WeatherWidget() {
const { toast } = useToast();
const queryClient = useQueryClient();
const { data: weather, isLoading, error } = useQuery<WeatherResponse>({
queryKey: ['/api/weather/current'],
refetchInterval: 30 * 60 * 1000, // Refetch every 30 minutes
retry: 1,
});
// Mutation pour la géolocalisation
const geoLocationMutation = useMutation({
mutationFn: async (coordinates: { latitude: number; longitude: number }) => {
const response = await fetch('/api/weather/geolocation', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(coordinates),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Erreur de géolocalisation');
}
return response.json();
},
onSuccess: (data) => {
toast({
title: "Localisation mise à jour",
description: data.message,
});
// Actualiser les données météo
queryClient.invalidateQueries({ queryKey: ['/api/weather/current'] });
},
onError: (error: any) => {
toast({
title: "Erreur de géolocalisation",
description: error.message || "Impossible de déterminer votre localisation",
variant: "destructive",
});
},
});
// Fonction pour obtenir la géolocalisation
const handleGeolocation = () => {
if (!navigator.geolocation) {
toast({
title: "Géolocalisation non supportée",
description: "Votre navigateur ne supporte pas la géolocalisation",
variant: "destructive",
});
return;
}
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
geoLocationMutation.mutate({ latitude, longitude });
},
(error) => {
let message = "Erreur de géolocalisation";
switch (error.code) {
case error.PERMISSION_DENIED:
message = "Autorisation de géolocalisation refusée";
break;
case error.POSITION_UNAVAILABLE:
message = "Position non disponible";
break;
case error.TIMEOUT:
message = "Délai d'attente dépassé";
break;
}
toast({
title: "Erreur de géolocalisation",
description: message,
variant: "destructive",
});
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 300000, // 5 minutes
}
);
};
if (isLoading) {
return (
<Card className="bg-gradient-to-r from-slate-50 to-slate-100 dark:from-slate-800 dark:to-slate-900 border-slate-200 dark:border-slate-700 shadow-sm">
@@ -160,9 +246,25 @@ export default function WeatherWidget() {
<WeatherIcon condition={weather.currentYear?.condition || ''} size={24} />
</div>
<div className="flex flex-col">
<span className="text-slate-700 dark:text-slate-200 font-semibold text-sm">
{location.split(',')[0]}
</span>
<div className="flex items-center gap-2">
<span className="text-slate-700 dark:text-slate-200 font-semibold text-sm">
{location.split(',')[0]}
</span>
<Button
variant="ghost"
size="sm"
onClick={handleGeolocation}
disabled={geoLocationMutation.isPending}
className="h-6 w-6 p-0 hover:bg-slate-200 dark:hover:bg-slate-600"
title="Détecter ma localisation"
>
{geoLocationMutation.isPending ? (
<Loader2 className="h-3 w-3 animate-spin" />
) : (
<MapPin className="h-3 w-3" />
)}
</Button>
</div>
<span className="text-xs text-slate-500 dark:text-slate-400">
{translateToFrench(weather.currentYear?.condition || 'Inconnu')}
</span>