diff --git a/src/app/api/weather/route.ts b/src/app/api/weather/route.ts new file mode 100644 index 0000000..7594aa9 --- /dev/null +++ b/src/app/api/weather/route.ts @@ -0,0 +1,164 @@ +export const POST = async (req: Request) => { + try { + const body: { lat: number; lng: number } = await req.json(); + + if (!body.lat || !body.lng) { + return Response.json( + { + message: 'Invalid request.', + }, + { status: 400 }, + ); + } + + const res = await fetch( + `https://api.open-meteo.com/v1/forecast?latitude=${body.lat}&longitude=${body.lng}¤t=weather_code,temperature_2m,is_day,relative_humidity_2m,wind_speed_10m&timezone=auto`, + ); + + const data = await res.json(); + + if (data.error) { + console.error(`Error fetching weather data: ${data.reason}`); + return Response.json( + { + message: 'An error has occurred.', + }, + { status: 500 }, + ); + } + + const weather: { + temperature: number; + condition: string; + humidity: number; + windSpeed: number; + icon: string; + } = { + temperature: data.current.temperature_2m, + condition: '', + humidity: data.current.relative_humidity_2m, + windSpeed: data.current.wind_speed_10m, + icon: '', + }; + + const code = data.current.weather_code; + const isDay = data.current.is_day === 1; + const dayOrNight = isDay ? 'day' : 'night'; + + switch (code) { + case 0: + weather.icon = `clear-${dayOrNight}`; + weather.condition = 'Clear'; + break; + + case 1: + weather.condition = 'Mainly Clear'; + case 2: + weather.condition = 'Partly Cloudy'; + case 3: + weather.icon = `cloudy-1-${dayOrNight}`; + weather.condition = 'Cloudy'; + break; + + case 45: + weather.condition = 'Fog'; + case 48: + weather.icon = `fog-${dayOrNight}`; + weather.condition = 'Fog'; + break; + + case 51: + weather.condition = 'Light Drizzle'; + case 53: + weather.condition = 'Moderate Drizzle'; + case 55: + weather.icon = `rainy-1-${dayOrNight}`; + weather.condition = 'Dense Drizzle'; + break; + + case 56: + weather.condition = 'Light Freezing Drizzle'; + case 57: + weather.icon = `frost-${dayOrNight}`; + weather.condition = 'Dense Freezing Drizzle'; + break; + + case 61: + weather.condition = 'Slight Rain'; + case 63: + weather.condition = 'Moderate Rain'; + case 65: + weather.condition = 'Heavy Rain'; + weather.icon = `rainy-2-${dayOrNight}`; + break; + + case 66: + weather.condition = 'Light Freezing Rain'; + case 67: + weather.condition = 'Heavy Freezing Rain'; + weather.icon = 'rain-and-sleet-mix'; + break; + + case 71: + weather.condition = 'Slight Snow Fall'; + case 73: + weather.condition = 'Moderate Snow Fall'; + case 75: + weather.condition = 'Heavy Snow Fall'; + weather.icon = `snowy-2-${dayOrNight}`; + break; + + case 77: + weather.condition = 'Snow'; + weather.icon = `snowy-1-${dayOrNight}`; + break; + + case 80: + weather.condition = 'Slight Rain Showers'; + case 81: + weather.condition = 'Moderate Rain Showers'; + case 82: + weather.condition = 'Heavy Rain Showers'; + weather.icon = `rainy-3-${dayOrNight}`; + break; + + case 85: + weather.condition = 'Slight Snow Showers'; + case 86: + weather.condition = 'Moderate Snow Showers'; + case 87: + weather.condition = 'Heavy Snow Showers'; + weather.icon = `snowy-3-${dayOrNight}`; + break; + + case 95: + weather.condition = 'Thunderstorm'; + weather.icon = `scattered-thunderstorms-${dayOrNight}`; + break; + + case 96: + weather.condition = 'Thunderstorm with Slight Hail'; + case 99: + weather.condition = 'Thunderstorm with Heavy Hail'; + weather.icon = 'severe-thunderstorm'; + break; + + default: + weather.icon = `clear-${dayOrNight}`; + weather.condition = 'Clear'; + break; + } + + return Response.json(weather); + } catch (err) { + console.error('An error occurred while getting home widgets', err); + return Response.json( + { + message: 'An error has occurred.', + }, + { + status: 500, + }, + ); + } +}; diff --git a/src/components/EmptyChat.tsx b/src/components/EmptyChat.tsx index 838849f..8c6bdab 100644 --- a/src/components/EmptyChat.tsx +++ b/src/components/EmptyChat.tsx @@ -1,8 +1,9 @@ import { Settings } from 'lucide-react'; import EmptyChatMessageInput from './EmptyChatMessageInput'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { File } from './ChatWindow'; import Link from 'next/link'; +import WeatherWidget from './WeatherWidget'; const EmptyChat = ({ sendMessage, @@ -25,8 +26,6 @@ const EmptyChat = ({ files: File[]; setFiles: (files: File[]) => void; }) => { - const [isSettingsOpen, setIsSettingsOpen] = useState(false); - return (
@@ -49,6 +48,11 @@ const EmptyChat = ({ files={files} setFiles={setFiles} /> +
+
+ +
+
); diff --git a/src/components/WeatherWidget.tsx b/src/components/WeatherWidget.tsx new file mode 100644 index 0000000..afbc94e --- /dev/null +++ b/src/components/WeatherWidget.tsx @@ -0,0 +1,145 @@ +import { Cloud, Sun, CloudRain, CloudSnow, Wind } from 'lucide-react'; +import { useEffect, useState } from 'react'; + +const WeatherWidget = () => { + const [data, setData] = useState({ + temperature: 0, + condition: '', + location: '', + humidity: 0, + windSpeed: 0, + icon: '', + }); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const getApproxLocation = async () => { + const res = await fetch('https://ipwhois.app/json/'); + const data = await res.json(); + + return { + latitude: data.latitude, + longitude: data.longitude, + city: data.city, + }; + }; + + const getLocation = async ( + callback: (location: { + latitude: number; + longitude: number; + city: string; + }) => void, + ) => { + /* + // Geolocation doesn't give city so we'll country using ipapi for now + if (navigator.geolocation) { + const result = await navigator.permissions.query({ + name: 'geolocation', + }) + + if (result.state === 'granted') { + navigator.geolocation.getCurrentPosition(position => { + callback({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }) + }) + } else if (result.state === 'prompt') { + callback(await getApproxLocation()) + navigator.geolocation.getCurrentPosition(position => {}) + } else if (result.state === 'denied') { + callback(await getApproxLocation()) + } + } else { + callback(await getApproxLocation()) + } */ + callback(await getApproxLocation()); + }; + + getLocation(async (location) => { + const res = await fetch(`/api/weather`, { + method: 'POST', + body: JSON.stringify({ + lat: location.latitude, + lng: location.longitude, + }), + }); + + const data = await res.json(); + + if (res.status !== 200) { + console.error('Error fetching weather data'); + setLoading(false); + return; + } + + setData({ + temperature: data.temperature, + condition: data.condition, + location: location.city, + humidity: data.humidity, + windSpeed: data.windSpeed, + icon: data.icon, + }); + setLoading(false); + }); + }, []); + + return ( +
+ {loading ? ( + <> +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + ) : ( + <> +
+ {data.condition} + + {data.temperature}°C + +
+
+
+ + {data.location} + + + + {data.windSpeed} km/h + +
+ + {data.condition} + +
+ Humidity: {data.humidity}% + Now +
+
+ + )} +
+ ); +}; + +export default WeatherWidget;