├── .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 | [](#contributors-)
4 |
5 |
6 | 
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 |
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 |

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 |

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 |

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 |

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 |

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 |

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 |
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 |
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 |

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 |
29 | >
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/Utils/Img.jsx:
--------------------------------------------------------------------------------
1 | import useImage from '../../hooks/useImage'
2 |
3 | export default function Img({ className, src, alt, width, height }) {
4 | const { image } = useImage(src)
5 |
6 | return (
7 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Utils/Map.jsx:
--------------------------------------------------------------------------------
1 | import { memo, useContext, useEffect, useState } from 'react'
2 |
3 | import { GoogleMap } from '@react-google-maps/api'
4 | import { WeatherContext } from '../../context/WeatherContextProvider'
5 |
6 | function Map({ className }) {
7 | const { location, searchWeatherByLatLng } = useContext(WeatherContext)
8 | const [center, setCenter] = useState({ lat: 0, lng: 0 })
9 |
10 | useEffect(() => {
11 | if (location) {
12 | setCenter({ lat: location.lat, lng: location.lon })
13 | }
14 | }, [location])
15 | const handleClick = (n) => {
16 | searchWeatherByLatLng({
17 | lat: n.latLng.lat(),
18 | lng: n.latLng.lng()
19 | })
20 | }
21 | return (
22 |
28 | )
29 | }
30 |
31 | export default memo(Map)
32 |
--------------------------------------------------------------------------------
/src/components/Utils/SearchBar.jsx:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 |
3 | import PlacesAutocomplete from 'react-places-autocomplete/dist/PlacesAutocomplete'
4 | import { WeatherContext } from '../../context/WeatherContextProvider'
5 |
6 | export default function SearchBar() {
7 | const [input, setInput] = useState('')
8 | const { searchWeather } = useContext(WeatherContext)
9 | return (
10 |
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/Utils/TempsData.jsx:
--------------------------------------------------------------------------------
1 | import DownArrow from '../../img/DownArrow.png'
2 | import Temp from '../../img/Temp.png'
3 | import UpArrow from '../../img/UpArrow.png'
4 | import { WeatherContext } from '../../context/WeatherContextProvider'
5 | import { useContext } from 'react'
6 |
7 | export default function TempsData({ temp, max }) {
8 | const { degreeType } = useContext(WeatherContext)
9 | return (
10 |
11 |
12 | {temp}º{degreeType}
13 |
14 |
15 |

16 | {max ? (
17 |

18 | ) : (
19 |

24 | )}
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/Utils/WelcomeMessage.jsx:
--------------------------------------------------------------------------------
1 | import sunImage from '../../img/Sunny.svg'
2 | export default function WelcomeMessage() {
3 | return (
4 |
8 |

9 |
10 |
15 | Good morning!
16 |
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Utils/WindHumData.jsx:
--------------------------------------------------------------------------------
1 | import Img from './Img'
2 |
3 | export default function WindHumData({ src, data, text, title, newLine }) {
4 | return (
5 |
6 |

13 |
14 | {title} {newLine &&
}
15 | {data} {text}
16 |
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Utils/index.js:
--------------------------------------------------------------------------------
1 | import Img from './Img'
2 | import Map from './Map'
3 | import SearchBar from './SearchBar'
4 | import TempsData from './TempsData'
5 | import WelcomeMessage from './WelcomeMessage'
6 | import WindHumData from './WindHumData'
7 | export { Img, Map, SearchBar, TempsData, WindHumData, WelcomeMessage }
8 |
--------------------------------------------------------------------------------
/src/context/WeatherContextProvider.jsx:
--------------------------------------------------------------------------------
1 | import { createContext, useReducer } from 'react'
2 |
3 | import { OPTIONS } from '../components/Const'
4 | import WeatherReducer from './WeatherReducer'
5 |
6 | export const WeatherContext = createContext()
7 | const initialState = {
8 | error: '',
9 | current: {},
10 | forecast: {},
11 | location: {
12 | localtime: ''
13 | },
14 | address: '',
15 | initialCords: {},
16 | degreeType: 'C'
17 | }
18 |
19 | export default function WeatherContextProvider(props) {
20 | const [state, dispatch] = useReducer(WeatherReducer, initialState)
21 |
22 | const getWeather = async (location) => {
23 | const { latitude, longitude } = location
24 |
25 | dispatch({ type: 'SET_COORDS', payload: location })
26 | const url = `https://weatherapi-com.p.rapidapi.com/forecast.json?q=${latitude}%2C${longitude}&days=3`
27 | const defaultUrl =
28 | 'https://weatherapi-com.p.rapidapi.com/forecast.json?q=London&days=3'
29 | // const url = `https://weatherapi-com.p.rapidapi.com/forecast.json?q=${IPv4}&days=3`
30 | fetch(location == null ? defaultUrl : url, OPTIONS)
31 | .then((res) => res.json())
32 | .then((data) => {
33 | dispatch({
34 | type: 'GET_WEATHER',
35 | payload: data
36 | })
37 | const { location } = data
38 | const { name, region, country } = location
39 | dispatch({
40 | type: 'CHANGE_ADDRESS',
41 | payload: `${name}, ${region}, ${country}`
42 | })
43 | })
44 | }
45 | const searchWeather = ({ newAddress }) => {
46 | const url = `https://weatherapi-com.p.rapidapi.com/forecast.json?q=${newAddress}&days=3`
47 | try {
48 | fetch(url, OPTIONS)
49 | .then((res) => {
50 | if (!res.ok) {
51 | res.json().then((data) => {
52 | dispatch({
53 | type: 'SET_ERROR',
54 | payload: data.error.message
55 | })
56 | })
57 | throw new Error(res.statusText)
58 | } else {
59 | return res.json()
60 | }
61 | })
62 | .then((data) => {
63 | dispatch({
64 | type: 'SEARCH_WEATHER',
65 | payload: data
66 | })
67 | const { location } = data
68 | const { name, region, country } = location
69 | dispatch({
70 | type: 'CHANGE_ADDRESS',
71 | payload: `${name}, ${region}, ${country}`
72 | })
73 | })
74 | } catch (err) {
75 | console.log(err)
76 | }
77 | }
78 | const searchWeatherByLatLng = ({ lat, lng }) => {
79 | const url = `https://weatherapi-com.p.rapidapi.com/forecast.json?q=${lat}%2C${lng}&days=3`
80 | try {
81 | fetch(url, OPTIONS)
82 | .then((res) => {
83 | if (!res.ok) {
84 | res.json().then((data) => {
85 | dispatch({
86 | type: 'SET_ERROR',
87 | payload: data.error.message
88 | })
89 | })
90 | throw new Error(res.statusText)
91 | } else {
92 | return res.json()
93 | }
94 | })
95 | .then((data) => {
96 | dispatch({
97 | type: 'SEARCH_WEATHER',
98 | payload: data
99 | })
100 | const { location } = data
101 | const { name, region, country } = location
102 | dispatch({
103 | type: 'CHANGE_ADDRESS',
104 | payload: `${name}, ${region}, ${country}`
105 | })
106 | })
107 | } catch (err) {
108 | console.log(err)
109 | }
110 | }
111 | const refreshWeather = ({ address }) => {
112 | const url = `https://weatherapi-com.p.rapidapi.com/forecast.json?q=${address}&days=3`
113 | try {
114 | fetch(url, OPTIONS)
115 | .then((res) => res.json())
116 | .then((data) => {
117 | dispatch({
118 | type: 'REFRESH_WEATHER',
119 | payload: data
120 | })
121 | })
122 | } catch (err) {
123 | console.log(err)
124 | }
125 | }
126 | const changeDegreeType = (degreeType) => {
127 | dispatch({
128 | type: 'CHANGE_DEGREES',
129 | payload: degreeType
130 | })
131 | }
132 | return (
133 |
148 | {props.children}
149 |
150 | )
151 | }
152 |
--------------------------------------------------------------------------------
/src/context/WeatherReducer.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | CHANGE_ADDRESS,
3 | CHANGE_DEGREES,
4 | GET_WEATHER,
5 | REFRESH_WEATHER,
6 | SEARCH_WEATHER,
7 | SET_ERROR
8 | } from './types'
9 | export default function WeatherReducer(state, action) {
10 | switch (action.type) {
11 | case GET_WEATHER:
12 | return {
13 | ...state,
14 | current: action.payload.current,
15 | forecast: action.payload.forecast,
16 | location: action.payload.location
17 | }
18 | case REFRESH_WEATHER:
19 | return {
20 | ...state,
21 | current: action.payload.current,
22 | forecast: action.payload.forecast,
23 | location: action.payload.location
24 | }
25 | case SEARCH_WEATHER:
26 | return {
27 | ...state,
28 | current: action.payload.current,
29 | forecast: action.payload.forecast,
30 | location: action.payload.location
31 | }
32 | case CHANGE_ADDRESS:
33 | return {
34 | ...state,
35 | address: action.payload
36 | }
37 | case SET_ERROR:
38 | return {
39 | ...state,
40 | error: action.payload
41 | }
42 | case CHANGE_DEGREES:
43 | return {
44 | ...state,
45 | degreeType: action.payload
46 | }
47 |
48 | default:
49 | return state
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/context/types.js:
--------------------------------------------------------------------------------
1 | export const SEARCH_WEATHER = 'SEARCH_WEATHER'
2 | export const REFRESH_WEATHER = 'REFRESH_WEATHER'
3 | export const GET_WEATHER = 'GET_WEATHER'
4 | export const CHANGE_ADDRESS = 'CHANGE_ADDRESS'
5 | export const SET_ERROR = 'SET_ERROR'
6 | export const SET_COORDS = 'SET_COORDS'
7 | export const CHANGE_DEGREES = 'CHANGE_DEGREES'
8 |
--------------------------------------------------------------------------------
/src/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/hooks/useCurrentLocation.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | const useCurrentLocation = (options = {}) => {
4 | // store location in state
5 | const [location, setLocation] = useState()
6 | // store error message in state
7 | const [error, setError] = useState()
8 |
9 | // Success handler for geolocation's `getCurrentPosition` method
10 | const handleSuccess = (pos) => {
11 | const { latitude, longitude } = pos.coords
12 |
13 | setLocation({
14 | latitude,
15 | longitude
16 | })
17 | }
18 |
19 | // Error handler for geolocation's `getCurrentPosition` method
20 | const handleError = (error) => {
21 | setError(error.message)
22 | }
23 |
24 | useEffect(() => {
25 | const { geolocation } = navigator
26 |
27 | // If the geolocation is not defined in the used browser we handle it as an error
28 | if (!geolocation) {
29 | setError('Geolocation is not supported.')
30 | return
31 | }
32 |
33 | // Call Geolocation API
34 | geolocation.getCurrentPosition(handleSuccess, handleError, options)
35 | }, [options])
36 |
37 | return { location, error }
38 | }
39 |
40 | export default useCurrentLocation
41 |
--------------------------------------------------------------------------------
/src/hooks/useDate.jsx:
--------------------------------------------------------------------------------
1 | import { DAY, MONTHS } from '../components/Const'
2 |
3 | import { WeatherContext } from '../context/WeatherContextProvider'
4 | import { useContext } from 'react'
5 |
6 | const useDate = () => {
7 | const { location } = useContext(WeatherContext)
8 |
9 | const { localtime } = location || ''
10 | const time = localtime?.slice(-5)
11 | const month = localtime?.slice(5, 7)
12 | const day = localtime?.slice(8, 10)
13 | const year = localtime?.slice(0, 4)
14 | const currentDate = ` ${day} ${MONTHS[month]} ${year}`
15 | const weekday = new Date(currentDate).getDay() || 0
16 |
17 | const date = `${DAY[weekday] + ', ' + currentDate}`
18 | return { time, date }
19 | }
20 |
21 | export default useDate
22 |
--------------------------------------------------------------------------------
/src/hooks/useImage.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | const useImage = (fileName) => {
4 | const [loading, setLoading] = useState(true)
5 | const [error, setError] = useState(null)
6 | const [image, setImage] = useState(null)
7 |
8 | useEffect(() => {
9 | const fetchImage = async () => {
10 | try {
11 | const response = await import('../img/' + fileName + '.png')
12 | setImage(response.default)
13 | } catch (err) {
14 | setError(err)
15 | } finally {
16 | setLoading(false)
17 | }
18 | }
19 |
20 | fetchImage()
21 | }, [fileName])
22 |
23 | return {
24 | loading,
25 | error,
26 | image
27 | }
28 | }
29 |
30 | export default useImage
31 |
--------------------------------------------------------------------------------
/src/hooks/useWatchLocation.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from 'react'
2 |
3 | const useWatchLocation = (options = {}) => {
4 | // store location in state
5 | const [location, setLocation] = useState()
6 | // store error message in state
7 | const [error, setError] = useState()
8 | // save the returned id from the geolocation's `watchPosition` to be able to cancel the watch instance
9 | const locationWatchId = useRef(null)
10 |
11 | // Success handler for geolocation's `watchPosition` method
12 | const handleSuccess = (pos) => {
13 | const { latitude, longitude } = pos.coords
14 |
15 | setLocation({
16 | latitude,
17 | longitude
18 | })
19 | }
20 |
21 | // Error handler for geolocation's `watchPosition` method
22 | const handleError = (error) => {
23 | setError(error.message)
24 | }
25 |
26 | // Clears the watch instance based on the saved watch id
27 | const cancelLocationWatch = () => {
28 | const { geolocation } = navigator
29 |
30 | if (locationWatchId.current && geolocation) {
31 | geolocation.clearWatch(locationWatchId.current)
32 | }
33 | }
34 |
35 | useEffect(() => {
36 | const { geolocation } = navigator
37 |
38 | // If the geolocation is not defined in the used browser we handle it as an error
39 | if (!geolocation) {
40 | setError('Geolocation is not supported.')
41 | return
42 | }
43 |
44 | // Start to watch the location with the Geolocation API
45 | locationWatchId.current = geolocation.watchPosition(
46 | handleSuccess,
47 | handleError,
48 | options
49 | )
50 |
51 | // Clear the location watch instance when React unmounts the used component
52 | return cancelLocationWatch
53 | }, [options])
54 |
55 | return { location, cancelLocationWatch, error }
56 | }
57 |
58 | export default useWatchLocation
59 |
--------------------------------------------------------------------------------
/src/img/CloudThunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/CloudThunder.png
--------------------------------------------------------------------------------
/src/img/Cloudy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/Cloudy.png
--------------------------------------------------------------------------------
/src/img/DownArrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/DownArrow.png
--------------------------------------------------------------------------------
/src/img/IceSnow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/IceSnow.png
--------------------------------------------------------------------------------
/src/img/Mist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/Mist.png
--------------------------------------------------------------------------------
/src/img/Moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/Moon.png
--------------------------------------------------------------------------------
/src/img/PartlyCloudy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/PartlyCloudy.png
--------------------------------------------------------------------------------
/src/img/RainCloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/RainCloud.png
--------------------------------------------------------------------------------
/src/img/Snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/Snow.png
--------------------------------------------------------------------------------
/src/img/Sunny.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/Sunny.png
--------------------------------------------------------------------------------
/src/img/Sunny.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/img/Temp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/Temp.png
--------------------------------------------------------------------------------
/src/img/UpArrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/UpArrow.png
--------------------------------------------------------------------------------
/src/img/WindDegree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/WindDegree.png
--------------------------------------------------------------------------------
/src/img/humidity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/humidity.png
--------------------------------------------------------------------------------
/src/img/location.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/location.png
--------------------------------------------------------------------------------
/src/img/logo1_jpg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/logo1_jpg.png
--------------------------------------------------------------------------------
/src/img/logo1_svg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/img/logo2_jpg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/logo2_jpg.png
--------------------------------------------------------------------------------
/src/img/logo2_svg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/img/rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/rain.png
--------------------------------------------------------------------------------
/src/img/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/search.png
--------------------------------------------------------------------------------
/src/img/sunrise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/sunrise.png
--------------------------------------------------------------------------------
/src/img/sunset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/sunset.png
--------------------------------------------------------------------------------
/src/img/wind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikurotime/GeoWeather/617408711fdf0bdf65ac1862fb7285c83269329f/src/img/wind.png
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 | body {
5 | margin: 0;
6 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
7 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
8 | sans-serif;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | code {
14 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
15 | monospace;
16 | }
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import './index.css'
2 |
3 | import App from './App'
4 | import React from 'react'
5 | import ReactDOM from 'react-dom/client'
6 |
7 | ReactDOM.createRoot(document.getElementById('root')).render(
8 |
9 |
10 |
11 | )
12 |
--------------------------------------------------------------------------------
/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./src/**/*.{js,jsx,ts,tsx}'],
3 | theme: {
4 | extend: {
5 | colors: {
6 | cardGray: '#D0D0D0',
7 | blurple: '#362f5d'
8 | }
9 | }
10 | },
11 | plugins: []
12 | }
13 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()]
7 | })
8 |
--------------------------------------------------------------------------------