diff --git a/weather_app/app/components/airPollution/AirPollution.tsx b/weather_app/app/components/airPollution/AirPollution.tsx new file mode 100644 index 0000000..6a7fca0 --- /dev/null +++ b/weather_app/app/components/airPollution/AirPollution.tsx @@ -0,0 +1,31 @@ +"use client" +import { useGlobalContext } from '@/app/context/globalContext' +import { air_QualityIndex } from '@/app/utils/Misc'; +import { Progress } from '@/components/ui/progress'; +import { Skeleton } from '@/components/ui/skeleton'; +import React from 'react' +import { FaThermometerEmpty } from "react-icons/fa"; + +const AirPollution = () => { + const {airPollution} = useGlobalContext(); + if(!airPollution || !airPollution.list || !airPollution.list[0] || !airPollution.list[0].main){ + return + } + const airQualityIndex = airPollution.list[0].main.aqi * 10; + console.log(airQualityIndex) + //filter aqi + const filterAQI = air_QualityIndex.find((item) => { + return item.rating === airQualityIndex; + }) + + + return ( +
+

Air Pollution

+ +

Air quality is {filterAQI?.desc}

+
+ ) +} + +export default AirPollution \ No newline at end of file diff --git a/weather_app/app/components/daily_forcast/DailyForecast.tsx b/weather_app/app/components/daily_forcast/DailyForecast.tsx new file mode 100644 index 0000000..0df7ec4 --- /dev/null +++ b/weather_app/app/components/daily_forcast/DailyForecast.tsx @@ -0,0 +1,75 @@ +"use client" +import { useGlobalContext } from '@/app/context/globalContext' +import { Skeleton } from '@/components/ui/skeleton'; +import { CloudIcon, CloudRainIcon, SnowflakeIcon, SunIcon, Zap } from 'lucide-react'; +import React from 'react' +import { + Carousel, + CarouselContent, + CarouselItem, +} from "@/components/ui/carousel" +import moment from 'moment'; +import { kelvinToCelsius } from '@/app/utils/Misc'; + +const DailyForcast = () => { + const {forecast, dailyForecast} = useGlobalContext(); + const {weather} = forecast; + const {city, list} = dailyForecast; + if(!dailyForecast || !city || !list){ + return + } + if(!forecast || !weather){ + return + } + const today = new Date(); + const todayString = today.toISOString().split("T")[0]; + + const todayForecast = list.filter((forecast: {dt_txt: string; main: {temp: number}}) => { + return forecast.dt_txt.startsWith(todayString); + }) + + const {main: weatherMain} = weather[0] + const getIcon = () => { + switch (weatherMain){ + case "Snow": + return ; + case "Rain": + return ; + case "Clear": + return ; + case "Clouds": + return ; + case "Thunderstorm": + return ; + } +}; + + + return ( +
+
+ {todayForecast.length < 1 ? (
Loading...
) : + ( +
+ + + { + todayForecast.map((forecast: {dt_txt: string; main: {temp: number}}) => { + return +

{moment(forecast.dt_txt).format("hh:mm")}

+

{getIcon()}

+

{kelvinToCelsius(forecast.main.temp)}°F

+
+ }) + } +
+
+
+ ) + } +
+
+ ) +} + +export default DailyForcast \ No newline at end of file diff --git a/weather_app/app/components/sunset/Sunset.tsx b/weather_app/app/components/sunset/Sunset.tsx new file mode 100644 index 0000000..2a9b4e7 --- /dev/null +++ b/weather_app/app/components/sunset/Sunset.tsx @@ -0,0 +1,34 @@ +"use client" +import { useGlobalContext } from '@/app/context/globalContext' +import { unixToTime } from '@/app/utils/Misc'; +import { Skeleton } from '@/components/ui/skeleton'; +import React from 'react' +import { GiSunrise } from "react-icons/gi"; +import { GiSunset } from "react-icons/gi"; + +const Sunset = () => { + const {forecast} = useGlobalContext(); + if(!forecast || !forecast.sys || !forecast?.sys?.sunset){ + return + } + const times = forecast?.sys.sunset; + const timezone = forecast?.timezone; + const timeSunset = unixToTime(times, timezone) + const timeSunrise = unixToTime(forecast?.sys?.sunrise, timezone) + + + return ( +
+
+

Sunrise

+

{timeSunrise} am

+
+
+

Sunset

+

{timeSunset} pm

+
+
+ ) +} + +export default Sunset \ No newline at end of file diff --git a/weather_app/app/components/temp/Temperature.tsx b/weather_app/app/components/temp/Temperature.tsx new file mode 100644 index 0000000..c747a3f --- /dev/null +++ b/weather_app/app/components/temp/Temperature.tsx @@ -0,0 +1,79 @@ +"use client" +import { useGlobalContext } from '@/app/context/globalContext' +import { kelvinToCelsius } from '@/app/utils/Misc'; +import { CloudIcon, CloudRainIcon, SnowflakeIcon, SunIcon, Zap } from 'lucide-react'; +import { IoNavigateOutline } from "react-icons/io5"; + +import React, { useEffect, useState } from 'react' +import moment from 'moment'; + +const Temperature = () => { + const {forecast} = useGlobalContext(); + const {main, timezone, name, weather} = forecast; + if(!forecast || !weather){ + return
Loading...
+ } + const temp = kelvinToCelsius(main?.temp); + const minTemp = kelvinToCelsius(main?.temp_min); + const maxTemp = kelvinToCelsius(main?.temp_max); + + //set time, day, weather + const [localTime, setLocalTime] = useState(""); + const [currentDay, setCurrentDay] = useState(""); + const {main: weatherMain, description} = weather[0]; + const getIcon = () => { + switch (weatherMain){ + case "Snow": + return ; + case "Rain": + return ; + case "Clear": + return ; + case "Clouds": + return ; + case "Thunderstorm": + return ; + } + }; + + //real time update + useEffect(() => { + const interval = setInterval(() => { + const localMoment = moment().utcOffset(timezone / 60); + const formatedTime = localMoment.format("hh:mm:ss"); + const day = localMoment.format("dddd") + + setLocalTime(formatedTime); + setCurrentDay(day); + }, 1000); + }, []); + + + + + return ( +
+

+ {currentDay} + {localTime} +

+

+ {name} + +

+

{temp}°F

+
+
+ {getIcon()} +

{description}

+
+

+ Low: {minTemp}°F + Hight: {maxTemp}°F +

+
+
+ ) +} + +export default Temperature \ No newline at end of file diff --git a/weather_app/app/components/uvIndex/UVI.tsx b/weather_app/app/components/uvIndex/UVI.tsx new file mode 100644 index 0000000..402258c --- /dev/null +++ b/weather_app/app/components/uvIndex/UVI.tsx @@ -0,0 +1,12 @@ +"use client" +import React from 'react' + +const UVI = () => { + return ( +
+ UVI +
+ ) +} + +export default UVI \ No newline at end of file diff --git a/weather_app/app/components/wind/Wind.tsx b/weather_app/app/components/wind/Wind.tsx new file mode 100644 index 0000000..0f5df62 --- /dev/null +++ b/weather_app/app/components/wind/Wind.tsx @@ -0,0 +1,33 @@ +"use client" +import { useGlobalContext } from '@/app/context/globalContext' +import { Skeleton } from '@/components/ui/skeleton'; +import React from 'react' +import { LuWind } from "react-icons/lu"; +import { LiaCompass } from "react-icons/lia"; + +const Wind = () => { + const {forecast} = useGlobalContext(); + const windSpeed = forecast?.wind?.speed * 2.2369; + const windDirection = forecast?.wind?.deg; + if(!windSpeed || !windDirection){ + return + } + + return ( +
+

Wind

+
+

+

{Math.round(windSpeed)}

+
+

Wind

+

MPH

+
+ +
+ +
+ ) +} + +export default Wind \ No newline at end of file diff --git a/weather_app/components/ui/carousel.tsx b/weather_app/components/ui/carousel.tsx new file mode 100644 index 0000000..ec505d0 --- /dev/null +++ b/weather_app/components/ui/carousel.tsx @@ -0,0 +1,262 @@ +"use client" + +import * as React from "react" +import useEmblaCarousel, { + type UseEmblaCarouselType, +} from "embla-carousel-react" +import { ArrowLeft, ArrowRight } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +type CarouselApi = UseEmblaCarouselType[1] +type UseCarouselParameters = Parameters +type CarouselOptions = UseCarouselParameters[0] +type CarouselPlugin = UseCarouselParameters[1] + +type CarouselProps = { + opts?: CarouselOptions + plugins?: CarouselPlugin + orientation?: "horizontal" | "vertical" + setApi?: (api: CarouselApi) => void +} + +type CarouselContextProps = { + carouselRef: ReturnType[0] + api: ReturnType[1] + scrollPrev: () => void + scrollNext: () => void + canScrollPrev: boolean + canScrollNext: boolean +} & CarouselProps + +const CarouselContext = React.createContext(null) + +function useCarousel() { + const context = React.useContext(CarouselContext) + + if (!context) { + throw new Error("useCarousel must be used within a ") + } + + return context +} + +const Carousel = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & CarouselProps +>( + ( + { + orientation = "horizontal", + opts, + setApi, + plugins, + className, + children, + ...props + }, + ref + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === "horizontal" ? "x" : "y", + }, + plugins + ) + const [canScrollPrev, setCanScrollPrev] = React.useState(false) + const [canScrollNext, setCanScrollNext] = React.useState(false) + + const onSelect = React.useCallback((api: CarouselApi) => { + if (!api) { + return + } + + setCanScrollPrev(api.canScrollPrev()) + setCanScrollNext(api.canScrollNext()) + }, []) + + const scrollPrev = React.useCallback(() => { + api?.scrollPrev() + }, [api]) + + const scrollNext = React.useCallback(() => { + api?.scrollNext() + }, [api]) + + const handleKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "ArrowLeft") { + event.preventDefault() + scrollPrev() + } else if (event.key === "ArrowRight") { + event.preventDefault() + scrollNext() + } + }, + [scrollPrev, scrollNext] + ) + + React.useEffect(() => { + if (!api || !setApi) { + return + } + + setApi(api) + }, [api, setApi]) + + React.useEffect(() => { + if (!api) { + return + } + + onSelect(api) + api.on("reInit", onSelect) + api.on("select", onSelect) + + return () => { + api?.off("select", onSelect) + } + }, [api, onSelect]) + + return ( + +
+ {children} +
+
+ ) + } +) +Carousel.displayName = "Carousel" + +const CarouselContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { carouselRef, orientation } = useCarousel() + + return ( +
+
+
+ ) +}) +CarouselContent.displayName = "CarouselContent" + +const CarouselItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { orientation } = useCarousel() + + return ( +
+ ) +}) +CarouselItem.displayName = "CarouselItem" + +const CarouselPrevious = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollPrev, canScrollPrev } = useCarousel() + + return ( + + ) +}) +CarouselPrevious.displayName = "CarouselPrevious" + +const CarouselNext = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollNext, canScrollNext } = useCarousel() + + return ( + + ) +}) +CarouselNext.displayName = "CarouselNext" + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, +} diff --git a/weather_app/components/ui/progress.tsx b/weather_app/components/ui/progress.tsx new file mode 100644 index 0000000..ce6a922 --- /dev/null +++ b/weather_app/components/ui/progress.tsx @@ -0,0 +1,28 @@ +"use client" + +import * as React from "react" +import * as ProgressPrimitive from "@radix-ui/react-progress" + +import { cn } from "@/lib/utils" + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)) +Progress.displayName = ProgressPrimitive.Root.displayName + +export { Progress } diff --git a/weather_app/components/ui/skeleton.tsx b/weather_app/components/ui/skeleton.tsx new file mode 100644 index 0000000..01b8b6d --- /dev/null +++ b/weather_app/components/ui/skeleton.tsx @@ -0,0 +1,15 @@ +import { cn } from "@/lib/utils" + +function Skeleton({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ) +} + +export { Skeleton }