Skip to content

Commit

Permalink
feat(dashboard): add hourly & weekly forecast to dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
IdoBouskila authored Dec 1, 2024
2 parents aae2ff6 + b352c56 commit 6370864
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 120 deletions.
57 changes: 44 additions & 13 deletions client/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ a {
}

.clouds {
top: clamp(5vh, 8.5%, 20vh);
top: clamp(5vh, 7.5%, 20vh);
left: 10.5vw;
width: 10rem;
height: 10rem;
Expand Down Expand Up @@ -83,7 +83,7 @@ a {
position: relative;
border-radius: 16px;
width: clamp(80%, 800px, 950px);
height: clamp(550px, 80%, 700px);
height: clamp(650px, 75%, 700px);
background: rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(5px);
Expand Down Expand Up @@ -165,17 +165,15 @@ a {
main {
display: grid;
padding: 2rem;
--gap: 1.5rem;
--gap: 1.25rem;
--search-bar-height: 2.5rem;
--middle-column-width: 50%;
gap: var(--gap);
grid-template: 'searchbar . .'
'content content content';
width: 100%;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
grid-template-rows: var(--search-bar-height) minmax(0, 1fr);
grid-template-columns: minmax(0, 1fr) var(--middle-column-width) minmax(0, 1fr);

}

.dashboard {
Expand All @@ -188,9 +186,10 @@ main {
min-height: 0;
gap: var(--gap);
grid-area: content;
grid-template: 'current-forecast map popular-cities'
grid-template:
'current-forecast map popular-cities'
'weekly-forecast hourly-forecast hourly-forecast';
grid-template-rows: var(--dashboard-first-row-height) minmax(0, 1fr);
grid-template-rows: var(--dashboard-first-row-height) minmax(0, 1fr);
grid-template-columns: minmax(0, 1fr) var(--middle-column-width) minmax(0, 1fr);
}

Expand Down Expand Up @@ -322,21 +321,21 @@ main {
}

.weather-degrees-container .degrees {
font-size: clamp(2vw, 4.5rem, 4vw);
font-size: clamp(2vw, 4rem, 4vw);
font-weight: 600;
}

.weather-degrees-container .degrees::after {
font-size: 1rem;
font-weight: 400;
inset: 0 -0.75rem auto auto;
margin-left: 5px;
}

.weather-degrees-container .weather-description {
font-weight: 300;
width: max-content;
}


.weather-details {
display: flex;
justify-content: space-between;
Expand Down Expand Up @@ -371,15 +370,23 @@ main {

.popular-cities .cities {
flex: 1;
gap: 2rem;
gap: 1rem;
display: flex;
overflow: auto;
flex-direction: column;
}

.popular-cities .cities div {
display: flex;
cursor: pointer;
border-radius: 5rem;
padding: 0.5rem 0.5rem;
justify-content: space-between;
transition: opacity 0.15s ease-in-out;
}

.popular-cities .cities div:hover {
opacity: 0.6;
}

.popular-cities .cities h2,
Expand Down Expand Up @@ -412,15 +419,16 @@ main {
font-weight: 300;
padding: 8px 10px;
align-items: center;
justify-content: start;
border-radius: 0.5rem;
}

.weekly-forecast li:hover {
background: rgba(0, 0, 0, 0.2);
}

.weekly-forecast li span.date {
margin-left: auto;
.weekly-forecast li span:nth-child(2) {
margin-right: 5px;
}

.weekly-forecast li img {
Expand Down Expand Up @@ -484,3 +492,26 @@ main {
50% { transform: translate(20px, -10px); }
}

@media (max-width: 1280px) {
html {
font-size: 14px;
}

.app-container {
width: 80%;
}
}

@media (max-width: 1080px) {
.app-container {
width: 90%;
}
}

/* Support for tablet & mobile */
@media (max-width: 840px) {
.app-container {
width: 100%;
height: 100%;
}
}
12 changes: 9 additions & 3 deletions client/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import './App.css';
import tabs from '@utils/tabs';
import { useState } from 'react';
import { Suspense, useState } from 'react';
import SideNavigation from '@components/side-navigation';
import SearchBar from '@components/search-bar/search-bar';
import { FavoritesProvider } from '@context/favorites-provider';

function App() {
const [currentTab, setCurrentTab] = useState(tabs[0]);
const [selectedLocation, setSelectedLocation] = useState('Jerusalem');

return (
<>
Expand All @@ -20,9 +21,14 @@ function App() {

<main>
<FavoritesProvider>
<SearchBar />
<SearchBar setSelectedLocation={ setSelectedLocation } />

<currentTab.component />
<Suspense fallback={ <div>Loading...</div> }>
<currentTab.component
selectedLocation={ selectedLocation }
setSelectedLocation={ setSelectedLocation }
/>
</Suspense>
</FavoritesProvider>
</main>
</div>
Expand Down
76 changes: 76 additions & 0 deletions client/src/components/current-weather-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { getHours } from '@utils/utility-functions';
import WeatherDetails from '@components/shared/weather-details';
import { PiEyeLight, PiSunLight, PiDropLight, PiWindLight } from 'react-icons/pi';

type Props = {
uv: number;
name: string;
wind: number;
degrees: number;
humidity: number;
localTime: string;
visibility: number;
description: string;
}

const CurrentWeatherCard: React.FC<Props> = ({
uv,
name,
wind,
degrees,
humidity,
localTime,
visibility,
description,
}) => {
const formattedTime = getHours(localTime);

return (
<div className='current-weather-container'>
<div className='title-container'>
<h1>{ name }</h1>
<span className='time'>{ formattedTime }</span>
</div>

<div className='weather-degrees-container'>
<img
src='/3d-weather-icon.png'
alt='weather-icon'
/>

<div>
<span className='degrees'>{ degrees }</span>
<span className='weather-description'>{ description }</span>
</div>
</div>

<div className='weather-details'>
<WeatherDetails
unit='km/h'
value={ wind }
icon={ <PiWindLight /> }
/>

<WeatherDetails
unit='%'
value={ humidity }
icon={ <PiDropLight /> }
/>

<WeatherDetails
unit=''
value={ uv }
icon={ <PiSunLight /> }
/>

<WeatherDetails
unit='km'
value={ visibility }
icon={ <PiEyeLight /> }
/>
</div>
</div>
)
}

export default CurrentWeatherCard;
123 changes: 26 additions & 97 deletions client/src/components/dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,109 +1,38 @@
import Map from './map';
import { trpc } from '@utils/trpc';
import WeeklyForecast from './weekly-forecast';
import HourlyForecast from './hourly-forecast';
import PopularLocations from './popular-locations';
import { PiWind, PiEyeLight, PiSunLight, PiDropLight, PiWindLight, PiThermometerSimple } from 'react-icons/pi';
import CurrentWeatherCard from './current-weather-card';

const Dashboard = () => {
return (
<div className='dashboard'>
<div className='current-weather-container'>
<div className='title-container'>
<h1>Current Weather</h1>
<span className='time'>18:15</span>
</div>

<div className='weather-degrees-container'>
<img
src='/3d-weather-icon.png'
alt='weather-icon'
/>

<div>
<span className='degrees'>25</span>
<span className='weather-description'>Clear Sky</span>
</div>
</div>

<div className='weather-details'>
<div>
<PiWindLight />
<span>5km/h</span>
</div>

<div>
<PiDropLight />
<span>80%</span>
</div>

<div>
<PiSunLight />
<span>3</span>
</div>

<div>
<PiEyeLight />
<span>10km</span>
</div>
</div>
</div>
const Dashboard: React.FC<{
selectedLocation: string;
setSelectedLocation: React.Dispatch<React.SetStateAction<string>>;
}> = ({ selectedLocation, setSelectedLocation }) => {
const [data] = trpc.getWeeklyForecast.useSuspenseQuery(selectedLocation);

<Map coordinates={{ lat: 51.5, lng: 0.12 }} />
const { current, hourly, forecast, location } = data;

<PopularLocations />

<div className='weekly-forecast'>
<h1>Forecast</h1>

<ul>
{
Array.from({ length: 7 }).map((_, index) => (
<li key={ index }>
<img src='/3d-weather-icon.png' alt='weather-icon' />

<span>
24° / 18°
</span>

<span className='date'>
25 Jul, Thu
</span>
</li>
))
}
</ul>
</div>

<div className='hourly-forecast'>
<h1>Hourly Forecast</h1>

<div>
{
Array.from({ length: 5 }).map((_, index) => (
<div key={ index } className='weather-item glassmorphism'>
<span className='weather-item-time'>7 PM</span>
return (
<div className='dashboard'>
<CurrentWeatherCard
uv={ current.uv }
wind={ current.wind }
name={ location.name }
humidity={ current.humidity }
localTime={ location.localtime }
visibility={ current.visibility }
description={ current.description }
degrees={ Math.round(current.temp_c) }
/>

<span className='weather-item-temp'>20°</span>
<Map coordinates={{ lat: location.lat, lng: location.lng }} />

<div className='weather-details'>
<div>
<PiThermometerSimple className='small-icon' />
<span>Feels 22°</span>
</div>
<PopularLocations setSelectedLocation={ setSelectedLocation } />

<div>
<PiDropLight className='small-icon' />
<span>8%</span>
</div>
<WeeklyForecast forecast={ forecast } />

<div>
<PiWind className='small-icon' />
<span>20 km/h</span>
</div>
</div>
</div>
))
}
</div>
</div>
<HourlyForecast forecast={ hourly } />
</div>
);
};
Expand Down
Loading

0 comments on commit 6370864

Please sign in to comment.