Custom app launcher, browser home page, and service monitor. cute.haus
homelab react typescript
0

Configure Feed

Select the types of activity you want to include in your feed.

add weather component

Aly Raffauf (Jan 29, 2026, 1:39 PM EST) c89ea483 0db66aa0

+66 -2
+8
src/App.tsx
··· 1 1 import Layout from "./Layout"; 2 2 import ServiceGrid from "./components/ServiceGrid"; 3 + import { Weather } from "./components/Weather"; 3 4 import { apps } from "./data/apps"; 4 5 import { privateApps } from "./data/privateApps"; 5 6 import { websites } from "./data/websites"; ··· 7 8 export function App() { 8 9 return ( 9 10 <Layout> 11 + <div className="grid gap-4 md:grid-cols-2"> 12 + <div> 13 + {" "} 14 + <h2 className="text-2xl font-semibold text-zinc-400 mb-4">Weather</h2> 15 + <Weather /> 16 + </div> 17 + </div> 10 18 <h2 className="text-2xl font-semibold text-zinc-400 mt-8 mb-4"> 11 19 Websites 12 20 </h2>
+42
src/components/Weather.tsx
··· 1 + import { useState, useEffect } from "react"; 2 + 3 + export function Weather() { 4 + const [weather, setWeather] = useState<{ 5 + temperature: number; 6 + temperatureUnit: string; 7 + shortForecast: string; 8 + } | null>(null); 9 + 10 + const [error, setError] = useState<string | null>(null); 11 + 12 + useEffect(() => { 13 + const fetchWeather = (lat: number, lon: number) => { 14 + fetch(`/api/weather?lat=${lat}&lon=${lon}`) 15 + .then((response) => response.json()) 16 + .then((data) => setWeather(data)); 17 + }; 18 + 19 + navigator.geolocation.getCurrentPosition( 20 + (position) => { 21 + fetchWeather(position.coords.latitude, position.coords.longitude); 22 + }, 23 + () => { 24 + // Fallback to Atlanta 25 + fetchWeather(33.749, -84.388); 26 + }, 27 + ); 28 + }, []); 29 + 30 + if (!weather) { 31 + return <div>Loading weather...</div>; 32 + } 33 + 34 + return ( 35 + <div className="block p-4 bg-zinc-800 border border-zinc-700 rounded-lg"> 36 + <div> 37 + {weather.temperature}°{weather.temperatureUnit} 38 + </div> 39 + <div>{weather.shortForecast}</div> 40 + </div> 41 + ); 42 + }
+16 -2
src/index.ts
··· 26 26 } 27 27 28 28 const server = serve({ 29 + hostname: "0.0.0.0", 30 + port: 3000, 29 31 routes: { 30 32 // Serve index.html for all unmatched routes. 31 33 "/*": index, ··· 37 39 }, 38 40 }, 39 41 40 - "/api/websites": { 42 + "/api/weather": { 41 43 async GET(req) { 42 - return Response.json(await checkStatuses(websites)); 44 + const url = new URL(req.url); 45 + const lat = url.searchParams.get("lat"); 46 + const lon = url.searchParams.get("lon"); 47 + 48 + const pointsResponse = await fetch( 49 + `https://api.weather.gov/points/${lat},${lon}`, 50 + ); 51 + 52 + const pointsData = await pointsResponse.json(); 53 + const forecastUrl = pointsData.properties.forecast; 54 + const forecastResponse = await fetch(forecastUrl); 55 + const forecastData = await forecastResponse.json(); 56 + return Response.json(forecastData.properties.periods[0]); 43 57 }, 44 58 }, 45 59