├── .all-contributorsrc ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.cjs ├── public ├── banner.png └── readme-banner.png ├── src ├── App.css ├── App.jsx ├── components │ ├── Const.js │ ├── Current │ │ ├── CurrentDayCard.jsx │ │ ├── CurrentDayChart.jsx │ │ ├── CurrentHourCard.jsx │ │ └── index.js │ ├── Forecast │ │ ├── ForecastCards.jsx │ │ ├── ForecastDayCard.jsx │ │ ├── ForecastDayChart.jsx │ │ └── index.js │ ├── HeaderFooter │ │ ├── Footer.jsx │ │ └── Header.jsx │ ├── Loading.jsx │ ├── MainPanel.jsx │ ├── Routes.jsx │ └── Utils │ │ ├── Img.jsx │ │ ├── Map.jsx │ │ ├── SearchBar.jsx │ │ ├── TempsData.jsx │ │ ├── WelcomeMessage.jsx │ │ ├── WindHumData.jsx │ │ └── index.js ├── context │ ├── WeatherContextProvider.jsx │ ├── WeatherReducer.jsx │ └── types.js ├── favicon.svg ├── hooks │ ├── useCurrentLocation.jsx │ ├── useDate.jsx │ ├── useImage.jsx │ └── useWatchLocation.jsx ├── img │ ├── CloudThunder.png │ ├── Cloudy.png │ ├── DownArrow.png │ ├── IceSnow.png │ ├── Mist.png │ ├── Moon.png │ ├── PartlyCloudy.png │ ├── RainCloud.png │ ├── Snow.png │ ├── Sunny.png │ ├── Sunny.svg │ ├── Temp.png │ ├── UpArrow.png │ ├── WindDegree.png │ ├── humidity.png │ ├── location.png │ ├── logo1_jpg.png │ ├── logo1_svg.svg │ ├── logo2_jpg.png │ ├── logo2_svg.svg │ ├── rain.png │ ├── search.png │ ├── sunrise.png │ ├── sunset.png │ └── wind.png ├── index.css ├── logo.svg └── main.jsx ├── tailwind.config.cjs └── vite.config.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "commitConvention": "angular", 8 | "contributors": [ 9 | { 10 | "login": "KaranSinghBisht", 11 | "name": "Karan Singh Bisht", 12 | "avatar_url": "https://avatars.githubusercontent.com/u/69008866?v=4", 13 | "profile": "https://github.com/KaranSinghBisht", 14 | "contributions": [ 15 | "code" 16 | ] 17 | }, 18 | { 19 | "login": "nebocoder", 20 | "name": "Nemanja", 21 | "avatar_url": "https://avatars.githubusercontent.com/u/91620216?v=4", 22 | "profile": "https://github.com/nebocoder", 23 | "contributions": [ 24 | "doc" 25 | ] 26 | }, 27 | { 28 | "login": "carlosfernandezcabrero", 29 | "name": "Carlos Fernandez Cabrero", 30 | "avatar_url": "https://avatars.githubusercontent.com/u/56631428?v=4", 31 | "profile": "https://es.linkedin.com/in/carlos-fern%C3%A1ndez-cabrero-08638a188", 32 | "contributions": [ 33 | "code" 34 | ] 35 | } 36 | ], 37 | "contributorsPerLine": 7, 38 | "skipCi": true, 39 | "repoType": "github", 40 | "repoHost": "https://github.com", 41 | "projectName": "GeoWeather", 42 | "projectOwner": "ikurotime", 43 | "commitType": "docs" 44 | } 45 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true 5 | }, 6 | extends: ['plugin:react/recommended', 'plugin:react/jsx-runtime', 'standard'], 7 | parserOptions: { 8 | ecmaFeatures: { 9 | jsx: true 10 | }, 11 | ecmaVersion: 'latest', 12 | sourceType: 'module' 13 | }, 14 | plugins: ['react'], 15 | rules: { 16 | 'space-before-function-paren': [ 17 | 'error', 18 | { 19 | anonymous: 'always', 20 | named: 'never', 21 | asyncArrow: 'always' 22 | } 23 | ], 24 | 'react/prop-types': 'off', 25 | 'multiline-ternary': 'off' 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeoWeather 2 | 3 | [![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-) 4 | 5 | 6 | ![GeoWeather Banner](public/readme-banner.png) 7 | 8 | A weather app for [Midudev](https://www.twitch.tv/midudev)'s Hackathon built with React. 9 | 10 | ## Features ✨ 11 | 12 | - Autocomplete search 13 | - Map 14 | - Current day weather data 15 | - Current day data by hour 16 | - Forecast day weather 17 | - Forecast day data by hour 18 | - Celsius / Farenheit 19 | - Temperature Chart 20 | - Dark Mode 21 | 22 | ## Installation 🔧 23 | 24 | Install GeoWeather 25 | 26 | - Go to the project folder 27 | - Run the following commands 28 | 29 | ```bash 30 | npm install 31 | npm run dev 32 | ``` 33 | 34 | - Go to `index.html` 35 | - Replace the API Key from Google Maps API with your key, available [here](https://mapsplatform.google.com/intl/es-419_ALL/) 36 | 37 | ``` 38 | 41 | ``` 42 | 43 | ## Contributors ✨ 44 | 45 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
Karan Singh Bisht
Karan Singh Bisht

💻
Nemanja
Nemanja

📖
Carlos Fernandez Cabrero
Carlos Fernandez Cabrero

💻
59 | 60 | 61 | 62 | 63 | 64 | 65 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 66 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | GeoWeather 24 | 25 | 26 | 30 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 49 | 53 | 54 | 55 | 60 | 64 | 65 | 66 |
67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather-web", 3 | "private": true, 4 | "version": "1.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "lint": "eslint .", 9 | "fix": "eslint . --fix", 10 | "host": "vite --host", 11 | "build": "vite build", 12 | "preview": "vite preview" 13 | }, 14 | "dependencies": { 15 | "@react-google-maps/api": "^2.11.8", 16 | "chart.js": "^3.8.0", 17 | "react": "^18.0.0", 18 | "react-chartjs-2": "^4.1.0", 19 | "react-dom": "^18.0.0", 20 | "react-places-autocomplete": "^7.3.0" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.0.0", 24 | "@types/react-dom": "^18.0.0", 25 | "@vitejs/plugin-react": "^1.3.0", 26 | "autoprefixer": "^10.4.7", 27 | "eslint": "^8.15.0", 28 | "eslint-config-standard": "^17.0.0", 29 | "eslint-plugin-import": "^2.26.0", 30 | "eslint-plugin-n": "^15.2.0", 31 | "eslint-plugin-promise": "^6.0.0", 32 | "eslint-plugin-react": "^7.30.0", 33 | "postcss": "^8.4.14", 34 | "tailwindcss": "^3.0.24", 35 | "vite": "^2.9.9" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/public/banner.png -------------------------------------------------------------------------------- /public/readme-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/public/readme-banner.png -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | button { 41 | font-size: calc(10px + 2vmin); 42 | } 43 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | CategoryScale, 3 | Chart as ChartJS, 4 | LineElement, 5 | LinearScale, 6 | PointElement, 7 | Title, 8 | Tooltip 9 | } from 'chart.js' 10 | 11 | import Routes from './components/Routes' 12 | import WeatherContextProvider from './context/WeatherContextProvider' 13 | 14 | function App() { 15 | ChartJS.register( 16 | CategoryScale, 17 | LinearScale, 18 | PointElement, 19 | LineElement, 20 | Title, 21 | Tooltip 22 | ) 23 | 24 | return ( 25 | 26 | 27 | 28 | ) 29 | } 30 | 31 | export default App 32 | -------------------------------------------------------------------------------- /src/components/Const.js: -------------------------------------------------------------------------------- 1 | export const MONTHS = { 2 | '01': 'January', 3 | '02': 'February', 4 | '03': 'March', 5 | '04': 'April', 6 | '05': 'May', 7 | '06': 'June', 8 | '07': 'July', 9 | '08': 'August', 10 | '09': 'September', 11 | 10: 'October', 12 | 11: 'November', 13 | 12: 'December' 14 | } 15 | export const DAY = { 16 | 0: 'Sunday', 17 | 1: 'Monday', 18 | 2: 'Tuesday', 19 | 3: 'Wednesday', 20 | 4: 'Thursday', 21 | 5: 'Friday', 22 | 6: 'Saturday' 23 | } 24 | export const OPTIONS = { 25 | method: 'GET', 26 | headers: { 27 | 'X-RapidAPI-Host': 'weatherapi-com.p.rapidapi.com', 28 | 'X-RapidAPI-Key': '2c9def7ff9msh9694b4a32b1be6cp146ff6jsnd33a36f39afd' 29 | } 30 | } 31 | export const WEATHER_CODES = { 32 | 1000: ['Sunny', 'Moon'], 33 | 1003: ['PartlyCloudy'], 34 | 1009: ['Cloudy'], 35 | 1030: ['Mist'], 36 | 1006: ['Cloudy'], 37 | 1063: ['RainCloud'], 38 | 1066: ['Snow'], 39 | 1069: ['IceSnow'], 40 | 1072: ['IceSnow'], 41 | 1087: ['CloudThunder'], 42 | 1114: ['Snow'], 43 | 1117: ['Snow'], 44 | 1135: ['Mist'], 45 | 1147: ['Mist'], 46 | 1150: ['RainCloud'], 47 | 1153: ['RainCloud'], 48 | 1168: ['IceSnow'], 49 | 1171: ['IceSnow'], 50 | 1180: ['RainCloud'], 51 | 1183: ['RainCloud'], 52 | 1186: ['RainCloud'], 53 | 1189: ['RainCloud'], 54 | 1192: ['RainCloud'], 55 | 1195: ['RainCloud'], 56 | 1198: ['IceSnow'], 57 | 1201: ['IceSnow'], 58 | 1204: ['IceSnow'], 59 | 1207: ['IceSnow'], 60 | 1210: ['Snow'], 61 | 1213: ['Snow'], 62 | 1216: ['Snow'], 63 | 1219: ['Snow'], 64 | 1222: ['Snow'], 65 | 1225: ['Snow'], 66 | 1237: ['IceSnow'], 67 | 1240: ['RainCloud'], 68 | 1243: ['RainCloud'], 69 | 1246: ['RainCloud'], 70 | 1249: ['IceSnow'], 71 | 1252: ['IceSnow'], 72 | 1255: ['Snow'], 73 | 1258: ['Snow'], 74 | 1261: ['IceSnow'], 75 | 1264: ['IceSnow'], 76 | 1273: ['RainCloud'], 77 | 1276: ['RainCloud'], 78 | 1279: ['Snow'], 79 | 1282: ['Snow'] 80 | } 81 | 82 | export const DAY_MESSAGES = { 83 | 1: "It's a rainy day. Take an umbrella.", 84 | 2: "It's a cloudy day. Take a jacket.", 85 | 3: "It's a sunny day. Take a hat.", 86 | 4: "It's a windy day. Take a scarf.", 87 | 5: "It's a snowy day. Take some warm clothes.", 88 | 6: "It's a foggy day. Be careful." 89 | } 90 | 91 | export const LABELS = [ 92 | '00:00', 93 | '01:00', 94 | '02:00', 95 | '03:00', 96 | '04:00', 97 | '05:00', 98 | '06:00', 99 | '07:00', 100 | '08:00', 101 | '09:00', 102 | '10:00', 103 | '11:00', 104 | '12:00', 105 | '13:00', 106 | '14:00', 107 | '15:00', 108 | '16:00', 109 | '17:00', 110 | '18:00', 111 | '19:00', 112 | '20:00', 113 | '21:00', 114 | '22:00', 115 | '23:00' 116 | ] 117 | export const geolocationOptions = { 118 | enableHighAccuracy: false, 119 | timeout: 1000 * 60 * 1, // 1 min (1000 ms * 60 sec * 1 minute = 60 000ms) 120 | maximumAge: 1000 * 3600 * 24 // 24 hour 121 | } 122 | export const DEFAULT_LOCATIONS = [ 123 | { latitude: 40.7128, longitude: -74.006 }, 124 | { latitude: 51.5074, longitude: -0.1278 }, 125 | { latitude: 48.8566, longitude: 2.3522 }, 126 | { latitude: 35.6895, longitude: 139.6917 }, 127 | { latitude: 55.7558, longitude: 37.6173 }, 128 | { latitude: 52.52, longitude: 13.405 }, 129 | { latitude: 37.7749, longitude: -122.4194 }, 130 | { latitude: 41.8781, longitude: -87.6298 }, 131 | { latitude: 39.9042, longitude: 116.4074 }, 132 | { latitude: 23.1291, longitude: 113.2644 }, 133 | { latitude: 28.6139, longitude: 77.209 }, 134 | { latitude: 19.076, longitude: 72.8777 } 135 | ] 136 | -------------------------------------------------------------------------------- /src/components/Current/CurrentDayCard.jsx: -------------------------------------------------------------------------------- 1 | import { DAY, WEATHER_CODES } from '../Const' 2 | 3 | import CurrentDayChart from './CurrentDayChart' 4 | import Img from '../Utils/Img' 5 | import Map from '../Utils/Map' 6 | import Temp from '../../img/Temp.png' 7 | import { WeatherContext } from '../../context/WeatherContextProvider' 8 | import WindHumData from '../Utils/WindHumData' 9 | import { useContext } from 'react' 10 | 11 | export default function CurrentDayCard() { 12 | const { current, degreeType, forecast } = useContext(WeatherContext) 13 | 14 | return ( 15 |
19 |
20 |
21 |

22 | {DAY[new Date(current.last_updated).getDay()] || 'Today'} 23 |

24 |

25 | {current.condition?.text} 26 |

27 |
28 | Weather 41 | 42 |
43 | 49 | 55 | 61 | 69 |
70 |
71 | 78 | 85 |
86 |
87 |
88 |

89 | {degreeType === 'C' 90 | ? current.temp_c + 'ºC' 91 | : current.temp_f + 'ºF'} 92 |

93 | Max temp 94 |
95 |
96 |
97 | 101 |
102 | ) 103 | } 104 | -------------------------------------------------------------------------------- /src/components/Current/CurrentDayChart.jsx: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useState } from 'react' 2 | 3 | import { LABELS } from '../Const' 4 | import { Line } from 'react-chartjs-2' 5 | import { WeatherContext } from '../../context/WeatherContextProvider' 6 | 7 | export default function CurrentDayChart() { 8 | const { degreeType, forecast } = useContext(WeatherContext) 9 | 10 | const [data, setData] = useState([]) 11 | useEffect(() => { 12 | setData([]) 13 | forecast?.forecastday?.[0]?.hour?.forEach((day, index) => { 14 | setData((prev) => [...prev, degreeType === 'C' ? day.temp_c : day.temp_f]) 15 | }) 16 | }, [forecast, degreeType]) 17 | 18 | const data2 = { 19 | labels: LABELS, 20 | datasets: [ 21 | { 22 | label: 'Temperature', 23 | backgroundColor: '#EAC435', 24 | borderColor: 'rgba(234, 196, 53,0.5)', 25 | data 26 | } 27 | ] 28 | } 29 | const options = { 30 | responsive: true, 31 | maintainAspectRatio: false, 32 | aspectRatio: 3, 33 | scales: { 34 | x: { 35 | ticks: { 36 | color: 'gray' 37 | } 38 | }, 39 | y: { 40 | ticks: { 41 | color: 'gray' 42 | } 43 | } 44 | } 45 | } 46 | 47 | return ( 48 |
49 | 50 |
51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /src/components/Current/CurrentHourCard.jsx: -------------------------------------------------------------------------------- 1 | import Img from '../Utils/Img' 2 | import { WEATHER_CODES } from '../Const' 3 | import { WeatherContext } from '../../context/WeatherContextProvider' 4 | import { useContext } from 'react' 5 | 6 | export default function CurrentHourCard({ element }) { 7 | const { degreeType } = useContext(WeatherContext) 8 | return ( 9 |
10 |
11 | Weather 23 |

24 | {element.time.slice(11, 16)} 25 |

26 |

27 | {degreeType === 'C' ? element.temp_c + 'ºC' : element.temp_f + 'ºF'} 28 |

29 |

30 | {element.condition?.text} 31 |

32 |
33 |
34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Current/index.js: -------------------------------------------------------------------------------- 1 | import CurrentDayCard from './CurrentDayCard' 2 | import CurrentDayChart from './CurrentDayChart' 3 | import CurrentHourCard from './CurrentHourCard' 4 | export { CurrentDayChart, CurrentDayCard, CurrentHourCard } 5 | -------------------------------------------------------------------------------- /src/components/Forecast/ForecastCards.jsx: -------------------------------------------------------------------------------- 1 | import { DAY, WEATHER_CODES } from '../Const' 2 | 3 | import Img from '../Utils/Img' 4 | import TempsData from '../Utils/TempsData' 5 | import { WeatherContext } from '../../context/WeatherContextProvider' 6 | import WindHumData from '../Utils/WindHumData' 7 | import { useContext } from 'react' 8 | 9 | export default function ForecastCards({ active, setActive }) { 10 | const { forecast, degreeType } = useContext(WeatherContext) 11 | 12 | return ( 13 |
17 | {forecast?.forecastday?.map((day, index) => ( 18 |
setActive(index)} 26 | > 27 |
28 |

29 | {DAY[new Date(day.date).getDay()]} 30 |

31 |

32 | {day.day.condition.text} 33 |

34 |
35 | Location 42 | 43 |
44 | 45 | 46 |
47 |
48 | 52 | 55 |
56 |
57 | )) ||
Loading...
} 58 |
59 | ) 60 | } 61 | -------------------------------------------------------------------------------- /src/components/Forecast/ForecastDayCard.jsx: -------------------------------------------------------------------------------- 1 | import { DAY, WEATHER_CODES } from '../Const' 2 | import { useContext, useEffect, useState } from 'react' 3 | 4 | import { ForecastDayChart } from '../Forecast' 5 | import { Img } from '../Utils' 6 | import Temp from '../../img/Temp.png' 7 | import { WeatherContext } from '../../context/WeatherContextProvider' 8 | import WindHumData from '../Utils/WindHumData' 9 | 10 | export default function ForecastDayCard({ active }) { 11 | const { forecast, degreeType } = useContext(WeatherContext) 12 | const day = forecast?.forecastday?.[active] 13 | 14 | const [data, setData] = useState([]) 15 | 16 | useEffect(() => { 17 | setData([]) 18 | day?.hour?.forEach((day, index) => { 19 | setData((prev) => [ 20 | ...prev, 21 | { 22 | x: index, 23 | y: degreeType === 'C' ? day.temp_c : day.temp_f 24 | } 25 | ]) 26 | }) 27 | }, [forecast, degreeType, active]) 28 | 29 | return ( 30 |
34 |
35 |
36 |

37 | {DAY[new Date(day?.date).getDay()]} 38 |

39 |

40 | {day?.day?.condition?.text} 41 |

42 |
43 |
44 | Weather 57 | 58 |
59 | 65 | 66 | 72 |
73 |
74 |
75 |

76 | {degreeType === 'C' 77 | ? day?.day?.avgtemp_c + '°C' 78 | : day?.day?.avgtemp_f + '°F'} 79 |

80 | Max temp 81 |
82 |
83 |
84 |
85 | ) 86 | } 87 | -------------------------------------------------------------------------------- /src/components/Forecast/ForecastDayChart.jsx: -------------------------------------------------------------------------------- 1 | import { LABELS } from '../Const' 2 | import { Line } from 'react-chartjs-2' 3 | 4 | export default function ForecastDayChart({ data }) { 5 | const data2 = { 6 | labels: LABELS, 7 | datasets: [ 8 | { 9 | label: 'Temperature', 10 | backgroundColor: '#EAC435', 11 | borderColor: 'rgba(234, 196, 53,0.5)', 12 | data 13 | } 14 | ] 15 | } 16 | const options = { 17 | responsive: true, 18 | maintainAspectRatio: false, 19 | aspectRatio: 3, 20 | plugins: { 21 | legend: { 22 | position: 'top', 23 | display: false 24 | } 25 | }, 26 | scales: { 27 | x: { 28 | ticks: { 29 | color: 'gray' 30 | } 31 | }, 32 | y: { 33 | ticks: { 34 | color: 'gray' 35 | } 36 | } 37 | } 38 | } 39 | 40 | return ( 41 |
42 | 43 |
44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /src/components/Forecast/index.js: -------------------------------------------------------------------------------- 1 | import ForecastCards from './ForecastCards' 2 | import ForecastDayCard from './ForecastDayCard' 3 | import ForecastDayChart from './ForecastDayChart' 4 | export { ForecastDayChart, ForecastDayCard, ForecastCards } 5 | -------------------------------------------------------------------------------- /src/components/HeaderFooter/Footer.jsx: -------------------------------------------------------------------------------- 1 | export default function Footer() { 2 | return ( 3 |
4 | 10 | davidhuertas.dev 11 | 12 |
13 |

14 | Made with ❤️  by{' '} 15 | 20 | David Huertas 21 | 22 |

23 |
24 | Icons by  25 | 26 | Icons8 27 | 28 |
29 |
30 | 36 | Github 37 | 38 |
39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /src/components/HeaderFooter/Header.jsx: -------------------------------------------------------------------------------- 1 | import { useContext, useState } from 'react' 2 | 3 | import { WeatherContext } from '../../context/WeatherContextProvider' 4 | 5 | export default function Header() { 6 | const { degreeType, changeDegreeType } = useContext(WeatherContext) 7 | const [degree, setDegree] = useState('C') 8 | 9 | const changeDegree = () => { 10 | if (degreeType === 'C') { 11 | setDegree('F') 12 | changeDegreeType('F') 13 | } else { 14 | setDegree('C') 15 | changeDegreeType('C') 16 | } 17 | } 18 | 19 | return ( 20 |
21 |

GeoWeather

22 |
23 | 30 | / 31 | 40 |
41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/components/Loading.jsx: -------------------------------------------------------------------------------- 1 | export default function Loading({ error }) { 2 | return ( 3 |
4 | {!error ? ( 5 | 12 | 16 | 20 | 21 | ) : ( 22 |

{error}

23 | )} 24 |
25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/components/MainPanel.jsx: -------------------------------------------------------------------------------- 1 | import { CurrentDayCard, CurrentHourCard } from './Current' 2 | import { ForecastCards, ForecastDayCard } from '../components/Forecast' 3 | import { useContext, useEffect, useState } from 'react' 4 | 5 | import { SearchBar } from '../components/Utils' 6 | import { WeatherContext } from '../context/WeatherContextProvider' 7 | import locationImage from '../img/location.png' 8 | import useDate from '../hooks/useDate' 9 | 10 | export default function MainPanel() { 11 | const [active, setActive] = useState(0) 12 | const { time, date } = useDate() 13 | const { address, forecast } = useContext(WeatherContext) 14 | useEffect(() => { 15 | const transition = () => { 16 | setTimeout(function () { 17 | const replacers = document.querySelectorAll('[data-replace]') 18 | for (let i = 0; i < replacers.length; i++) { 19 | const replaceClasses = JSON.parse( 20 | replacers[i].dataset.replace.replace(/'/g, '"') 21 | ) 22 | Object.keys(replaceClasses).forEach(function (key) { 23 | replacers[i].classList.remove(key) 24 | replacers[i].classList.add(replaceClasses[key]) 25 | }) 26 | } 27 | }, 100) 28 | } 29 | transition() 30 | }, []) 31 | 32 | return ( 33 |
34 | {location ? ( 35 |
36 |
40 |
41 |

42 | Local time: 43 |

44 |

45 | {time || ''} 46 |

47 |

48 | {date || ''}{' '} 49 |

50 |
51 | 52 |
53 | 54 |
55 | Location 61 |

62 | {address} 63 |

64 |
65 |
66 |
67 |

71 | Current Day 72 |

73 | 74 |
75 | {forecast?.forecastday?.[0].hour.map((element, index) => ( 76 | 77 | ))} 78 |
79 | 80 |

84 | Forecast 85 |

86 | 87 | 88 |
89 | {forecast?.forecastday?.[active].hour.map((element, index) => ( 90 | 91 | ))} 92 |
93 |
94 | ) : ( 95 |

Loading

96 | )} 97 |
98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /src/components/Routes.jsx: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from 'react' 2 | 3 | import { WeatherContext } from '../context/WeatherContextProvider' 4 | import useCurrentLocation from '../hooks/useCurrentLocation' 5 | import { DEFAULT_LOCATIONS, geolocationOptions } from './Const' 6 | import Footer from './HeaderFooter/Footer' 7 | import Header from './HeaderFooter/Header' 8 | import MainPanel from './MainPanel' 9 | 10 | export default function Routes() { 11 | const { location } = useCurrentLocation(geolocationOptions) 12 | const { getWeather } = useContext(WeatherContext) 13 | 14 | useEffect(() => { 15 | if (location !== undefined) { 16 | getWeather(location) 17 | } else { 18 | getWeather( 19 | DEFAULT_LOCATIONS[Math.floor(Math.random() * DEFAULT_LOCATIONS.length)] 20 | ) 21 | } 22 | }, [location]) 23 | 24 | return ( 25 | <> 26 |
27 | 28 |