diff --git a/app/actions.ts b/app/actions.ts index 7ea625f..568a6d5 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -1,5 +1,5 @@ 'use server' -import { Forecast, LocationType, coordType } from "@/types/types"; +import { Forecast, HourlyForecast, LocationType, coordType } from "@/types/types"; //takes address and returns coords in obj as {lat: number, lng: number} export async function getLocation(searchLocation: string): Promise{ @@ -26,3 +26,16 @@ export async function getForecast(geoLocation: coordType): Promise { const data: Forecast = await res.json(); return data; } + +export async function getHourlyForecast(geoLocation: coordType): Promise { + const { lat, lng } = geoLocation; + const appId = process.env.WEATHER_API; + const res = await fetch( + `https://pro.openweathermap.org/data/2.5/forecast/hourly?lat=${lat}&lon=${lng}&appid=${appId}&mode=json&` + ); + if (!res.ok) { + throw new Error(`Failed to fetch the weather data`); + } + const data: HourlyForecast = await res.json(); + return data; +} diff --git a/app/defaultState.ts b/app/defaultState.ts index 8d1bf77..51c62ab 100644 --- a/app/defaultState.ts +++ b/app/defaultState.ts @@ -1,3 +1,5 @@ +import { coordType } from "@/types/types"; + export const defaultState = { results: [ { @@ -93,3 +95,8 @@ export const defaultForecast = { name: "Copenhagen municipality", cod: 200, }; + +export const defaultGeoLocation: coordType = { + lat: 55.647229603577124, + lng: 12.54995987788925 +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 5ef58d9..d7acd02 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,27 +1,34 @@ 'use client' import LocationSearch from '@/components/LocationSearch'; import WeatherNow from '../components/WeatherNow' -import { useState } from 'react'; -import { defaultState } from './defaultState'; -import { coordType } from '@/types/types'; +import { createContext, useState } from 'react'; +import { defaultGeoLocation } from './defaultState'; +import { LocationContextType, coordType } from '@/types/types'; import WeatherIcon from '@/components/WeatherIcon'; -const defaultGeoLocation: coordType = { - lat: 55.647229603577124, - lng: 12.54995987788925 -} + +export const LocationContext = createContext({ + geoLocation: defaultGeoLocation, + setGeoLocation: () => {} // Default function, does nothing +}); export default function Home() { - const [geoLocation, setGeoLocation] = useState({ - lat: 55.647229603577124, - lng: 12.54995987788925 - }); + const [geoLocation, setGeoLocation] = useState(defaultGeoLocation); + + // Create an object that will be passed to the context provider + const contextValue: LocationContextType = { + geoLocation, + setGeoLocation + }; + return (
- - - + + + + +
); diff --git a/components/HourlyCard.tsx b/components/HourlyCard.tsx new file mode 100644 index 0000000..5cccb66 --- /dev/null +++ b/components/HourlyCard.tsx @@ -0,0 +1,11 @@ +import { LocationContext } from "@/app/page" +import { useContext } from "react" + + + +export default function HourlyCard() { + const { geoLocation } = useContext(LocationContext) + return ( +

Hejsa

+ ) +}; \ No newline at end of file diff --git a/components/LocationSearch.tsx b/components/LocationSearch.tsx index 588a06e..ce4583d 100644 --- a/components/LocationSearch.tsx +++ b/components/LocationSearch.tsx @@ -1,43 +1,47 @@ "use client"; +import React, { useContext, useState } from "react"; import { getLocation } from "@/app/actions"; -import { useState } from "react"; -import { useFormStatus } from "react-dom"; +import { LocationContext } from "@/app/page"; -export default function LocationSearch(props: any) { + +export default function LocationSearch() { const [searchLocation, setSearchLocation] = useState(""); - const setGeoLocation = props.setGeoLocation; - const { pending } = useFormStatus(); + const { geoLocation, setGeoLocation } = useContext(LocationContext); + const [pending, setPending] = useState(false); + const handleChange = (e: React.ChangeEvent) => { setSearchLocation(e.target.value); }; + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + setPending(true); try { const coordinates = await getLocation(searchLocation); setGeoLocation(coordinates); } catch (error) { console.error("Error fetching location:", error); } + setPending(false); }; return ( - <> -
- - - -
- +
+ + + +
); -} +} \ No newline at end of file diff --git a/components/WeatherIcon.tsx b/components/WeatherIcon.tsx index 2410d45..37e8fa8 100644 --- a/components/WeatherIcon.tsx +++ b/components/WeatherIcon.tsx @@ -1,5 +1,6 @@ import Image from "next/image"; import sunnyColor from '../public/sunny.svg' +import { useContext } from "react"; export default function WeatherIcon() { return( diff --git a/components/WeatherNow.tsx b/components/WeatherNow.tsx index 9248feb..fddf81a 100644 --- a/components/WeatherNow.tsx +++ b/components/WeatherNow.tsx @@ -3,15 +3,18 @@ import { getForecast } from "@/app/actions"; import Temperature from "./Temperature"; import { Forecast, coordType } from "@/types/types"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { defaultForecast } from "@/app/defaultState"; +import { LocationContext } from "@/app/page"; -export default function WeatherNow(props: { geoLocation: coordType }) { - // const weather = getForecast(props.geoLocation); + + +export default function WeatherNow() { + const { geoLocation } = useContext(LocationContext) const [weather, setWeather] = useState(defaultForecast); useEffect(() => { let mounted = true; - getForecast(props.geoLocation).then((data) => { + getForecast(geoLocation).then((data) => { if (mounted) { setWeather(data); } @@ -19,12 +22,11 @@ export default function WeatherNow(props: { geoLocation: coordType }) { return () => { mounted = false; }; - }, [props.geoLocation]); + }, [geoLocation]); return ( <>

Here is the current weather in {weather.name}

-

); } diff --git a/types/types.ts b/types/types.ts index e5d0b91..0794920 100644 --- a/types/types.ts +++ b/types/types.ts @@ -40,53 +40,108 @@ export interface Forecast { cod: number; } +export interface HourlyForecast { + cod: string; + message: number; + cnt: number; + list: Array<{ + dt: number; + main: { + temp: number; + feels_like: number; + temp_min: number; + temp_max: number; + pressure: number; + sea_level: number; + grnd_level: number; + humidity: number; + temp_kf: number; + }; + weather: Array<{ + id: number; + main: string; + description: string; + icon: string; + }>; + clouds: { + all: number; + }; + wind: { + speed: number; + deg: number; + gust: number; + }; + visibility: number; + pop: number; + rain?: { + "1h": number; + }; + sys: { + pod: string; + }; + dt_txt: string; + }>; + city: { + id: number; + name: string; + coord: { + lat: number; + lon: number; + }; + country: string; + population: number; + timezone: number; + sunrise: number; + sunset: number; + }; +} export interface LocationType { results: { - address_components: { - long_name: string; - short_name: string; - types: string[]; - }[]; - formatted_address: string; - geometry: { - bounds: { - northeast: { - lat: number; - lng: number; - }; - southwest: { - lat: number; - lng: number; - }; - }; - location: { - lat: number; - lng: number; - }; - location_type: string; - viewport: { - northeast: { - lat: number; - lng: number; - }; - southwest: { - lat: number; - lng: number; - }; - }; - }; - place_id: string; + address_components: { + long_name: string; + short_name: string; types: string[]; + }[]; + formatted_address: string; + geometry: { + bounds: { + northeast: { + lat: number; + lng: number; + }; + southwest: { + lat: number; + lng: number; + }; + }; + location: { + lat: number; + lng: number; + }; + location_type: string; + viewport: { + northeast: { + lat: number; + lng: number; + }; + southwest: { + lat: number; + lng: number; + }; + }; + }; + place_id: string; + types: string[]; }[]; status: string; -} +} export interface coordType { - lat: number; - lng: number; - } -export type EffectCallback = () => (void | (() => void | undefined)); + lat: number; + lng: number; +} +export type EffectCallback = () => void | (() => void | undefined); export interface TempInfo { temp: number; @@ -95,4 +150,10 @@ export interface TempInfo { temp_max: number; pressure: number; humidity: number; +} + + +export interface LocationContextType { + geoLocation: coordType; + setGeoLocation: React.Dispatch>; } \ No newline at end of file