├── finished-result ├── src │ ├── App.css │ ├── components │ │ ├── Form │ │ │ ├── index.js │ │ │ ├── Form.module.css │ │ │ └── Form.jsx │ │ ├── Error │ │ │ ├── index.js │ │ │ ├── Error.module.css │ │ │ └── Error.jsx │ │ ├── Loader │ │ │ ├── index.js │ │ │ ├── Loader.module.css │ │ │ └── Loader.jsx │ │ ├── Forecast │ │ │ ├── index.js │ │ │ ├── Forecast.module.css │ │ │ └── Forecast.jsx │ │ ├── CurrentDay │ │ │ ├── index.js │ │ │ ├── assets │ │ │ │ ├── mountain.jpg │ │ │ │ └── location-pin.png │ │ │ ├── CurrentDay.module.css │ │ │ └── CurrentDay.jsx │ │ ├── CurrentDayDescription │ │ │ ├── CurrentDayDescription.module.css │ │ │ ├── index.js │ │ │ └── CurrentDayDescription.jsx │ │ ├── Page │ │ │ ├── index.js │ │ │ ├── Page.module.css │ │ │ └── Page.jsx │ │ ├── CurrentDayDescriptionItem │ │ │ ├── CurrentDayDescriptionItem.module.css │ │ │ ├── index.js │ │ │ └── CurrentDayDescriptionItem.jsx │ │ ├── Header │ │ │ ├── index.js │ │ │ ├── Header.module.css │ │ │ └── Header.jsx │ │ ├── UpcomingDaysForecast │ │ │ ├── index.js │ │ │ ├── UpcomingDaysForecast.module.css │ │ │ └── UpcomingDaysForecast.jsx │ │ └── UpcomingDaysForecastItem │ │ │ ├── index.js │ │ │ ├── UpcomingDaysForecastItem.module.css │ │ │ └── UpcomingDaysForecastItem.jsx │ ├── App.js │ ├── index.js │ ├── helpers │ │ ├── getUpcomingDaysForecast.js │ │ ├── getCurrentDayForecast.js │ │ └── getCurrentDayDetailedForecast.js │ ├── index.css │ └── hooks │ │ └── useForecast.js ├── public │ ├── assets │ │ ├── favicon.ico │ │ └── apple-touch-icon.png │ ├── manifest.json │ └── index.html ├── .prettierrc ├── .gitignore └── package.json ├── starter-files ├── src │ ├── App.css │ ├── components │ │ ├── Error │ │ │ ├── index.js │ │ │ ├── Error.module.css │ │ │ └── Error.jsx │ │ ├── Form │ │ │ ├── index.js │ │ │ ├── Form.jsx │ │ │ └── Form.module.css │ │ ├── Forecast │ │ │ ├── index.js │ │ │ ├── Forecast.module.css │ │ │ └── Forecast.jsx │ │ ├── Loader │ │ │ ├── index.js │ │ │ ├── Loader.module.css │ │ │ └── Loader.jsx │ │ ├── CurrentDay │ │ │ ├── index.js │ │ │ ├── assets │ │ │ │ ├── mountain.jpg │ │ │ │ └── location-pin.png │ │ │ ├── CurrentDay.jsx │ │ │ └── CurrentDay.module.css │ │ ├── CurrentDayDescription │ │ │ ├── CurrentDayDescription.module.css │ │ │ ├── index.js │ │ │ └── CurrentDayDescription.jsx │ │ ├── Page │ │ │ ├── index.js │ │ │ ├── Page.jsx │ │ │ └── Page.module.css │ │ ├── CurrentDayDescriptionItem │ │ │ ├── CurrentDayDescriptionItem.module.css │ │ │ ├── index.js │ │ │ └── CurrentDayDescriptionItem.jsx │ │ ├── Header │ │ │ ├── index.js │ │ │ ├── Header.module.css │ │ │ └── Header.jsx │ │ ├── UpcomingDaysForecast │ │ │ ├── index.js │ │ │ ├── UpcomingDaysForecast.jsx │ │ │ └── UpcomingDaysForecast.module.css │ │ └── UpcomingDaysForecastItem │ │ │ ├── index.js │ │ │ ├── UpcomingDaysForecastItem.jsx │ │ │ └── UpcomingDaysForecastItem.module.css │ ├── App.js │ ├── index.js │ ├── helpers │ │ ├── getUpcomingDaysForecast.js │ │ ├── getCurrentDayForecast.js │ │ └── getCurrentDayDetailedForecast.js │ └── index.css ├── public │ ├── assets │ │ ├── favicon.ico │ │ └── apple-touch-icon.png │ ├── manifest.json │ └── index.html ├── .prettierrc ├── .gitignore └── package.json ├── preview.png └── README.md /finished-result/src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /starter-files/src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /finished-result/src/components/Form/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Form'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/Error/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Error'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/Form/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Form'; 2 | -------------------------------------------------------------------------------- /finished-result/src/components/Error/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Error'; 2 | -------------------------------------------------------------------------------- /finished-result/src/components/Loader/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Loader'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/Forecast/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Forecast'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/Loader/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Loader'; 2 | -------------------------------------------------------------------------------- /finished-result/src/components/Error/Error.module.css: -------------------------------------------------------------------------------- 1 | .error { 2 | bottom: 0; 3 | } 4 | -------------------------------------------------------------------------------- /finished-result/src/components/Forecast/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Forecast'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDay/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CurrentDay'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/Error/Error.module.css: -------------------------------------------------------------------------------- 1 | .error { 2 | bottom: 0; 3 | } 4 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDay/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CurrentDay'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDayDescription/CurrentDayDescription.module.css: -------------------------------------------------------------------------------- 1 | /* your css code */ -------------------------------------------------------------------------------- /starter-files/src/components/Page/index.js: -------------------------------------------------------------------------------- 1 | import Page from './Page'; 2 | 3 | export default Page; 4 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDayDescription/CurrentDayDescription.module.css: -------------------------------------------------------------------------------- 1 | /* your css code */ -------------------------------------------------------------------------------- /finished-result/src/components/Page/index.js: -------------------------------------------------------------------------------- 1 | import Page from './Page'; 2 | 3 | export default Page; 4 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDayDescriptionItem/CurrentDayDescriptionItem.module.css: -------------------------------------------------------------------------------- 1 | /* your css code */ -------------------------------------------------------------------------------- /finished-result/src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/preview.png -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDayDescriptionItem/CurrentDayDescriptionItem.module.css: -------------------------------------------------------------------------------- 1 | /* your css code */ -------------------------------------------------------------------------------- /starter-files/src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /starter-files/src/components/UpcomingDaysForecast/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './UpcomingDaysForecast'; 2 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDayDescription/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CurrentDayDescription'; 2 | -------------------------------------------------------------------------------- /finished-result/src/components/UpcomingDaysForecast/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './UpcomingDaysForecast'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDayDescription/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CurrentDayDescription'; 2 | -------------------------------------------------------------------------------- /finished-result/src/components/UpcomingDaysForecastItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './UpcomingDaysForecastItem'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDayDescriptionItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CurrentDayDescriptionItem'; 2 | -------------------------------------------------------------------------------- /starter-files/src/components/UpcomingDaysForecastItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './UpcomingDaysForecastItem'; 2 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDayDescriptionItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CurrentDayDescriptionItem'; 2 | -------------------------------------------------------------------------------- /starter-files/public/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/starter-files/public/assets/favicon.ico -------------------------------------------------------------------------------- /finished-result/public/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/finished-result/public/assets/favicon.ico -------------------------------------------------------------------------------- /starter-files/public/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/starter-files/public/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /finished-result/public/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/finished-result/public/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDay/assets/mountain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/starter-files/src/components/CurrentDay/assets/mountain.jpg -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDay/assets/mountain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/finished-result/src/components/CurrentDay/assets/mountain.jpg -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDay/assets/location-pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/starter-files/src/components/CurrentDay/assets/location-pin.png -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDay/assets/location-pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danascript/the-ultimate-api-challenge-weather-react/HEAD/finished-result/src/components/CurrentDay/assets/location-pin.png -------------------------------------------------------------------------------- /starter-files/src/components/Header/Header.module.css: -------------------------------------------------------------------------------- 1 | .heading { 2 | padding: 2rem; 3 | text-align: center; 4 | font-weight: 900; 5 | font-size: 2rem; 6 | } 7 | 8 | .light { 9 | font-weight: 100; 10 | } 11 | -------------------------------------------------------------------------------- /finished-result/src/components/Header/Header.module.css: -------------------------------------------------------------------------------- 1 | .heading { 2 | padding: 2rem; 3 | text-align: center; 4 | font-weight: 900; 5 | font-size: 2rem; 6 | } 7 | 8 | .light { 9 | font-weight: 100; 10 | } 11 | -------------------------------------------------------------------------------- /finished-result/src/App.js: -------------------------------------------------------------------------------- 1 | import Page from './components/Page'; 2 | 3 | function App() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | 11 | export default App; 12 | -------------------------------------------------------------------------------- /starter-files/src/App.js: -------------------------------------------------------------------------------- 1 | import Page from './components/Page'; 2 | 3 | function App() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | 11 | export default App; 12 | -------------------------------------------------------------------------------- /starter-files/src/components/Loader/Loader.module.css: -------------------------------------------------------------------------------- 1 | .box { 2 | height: 300px; 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | } 7 | 8 | .box svg path, 9 | .box svg rect { 10 | fill: #65b6ee; 11 | } 12 | -------------------------------------------------------------------------------- /finished-result/src/components/Loader/Loader.module.css: -------------------------------------------------------------------------------- 1 | .box { 2 | height: 300px; 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | } 7 | 8 | .box svg path, 9 | .box svg rect { 10 | fill: #65b6ee; 11 | } 12 | -------------------------------------------------------------------------------- /finished-result/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "printWidth": 120, 5 | "semi": true, 6 | "singleQuote": true, 7 | "tabWidth": 4, 8 | "trailingComma": "es5", 9 | "useTabs": false, 10 | "proseWrap": "never" 11 | } -------------------------------------------------------------------------------- /starter-files/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "printWidth": 120, 5 | "semi": true, 6 | "singleQuote": true, 7 | "tabWidth": 4, 8 | "trailingComma": "es5", 9 | "useTabs": false, 10 | "proseWrap": "never" 11 | } -------------------------------------------------------------------------------- /starter-files/src/components/Page/Page.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | 3 | import Header from '../Header'; 4 | 5 | const Page = () => { 6 | return ( 7 | 8 |
9 | 10 | ); 11 | }; 12 | 13 | export default Page; 14 | -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDayDescription/CurrentDayDescription.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const CurrentDayDescription = () => ( 4 |
5 |
6 |
7 | ); 8 | 9 | export default CurrentDayDescription; 10 | -------------------------------------------------------------------------------- /starter-files/src/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Header.module.css'; 4 | 5 | const Header = () => ( 6 |

7 | Weather Forecast 8 |

9 | ); 10 | 11 | export default Header; 12 | -------------------------------------------------------------------------------- /finished-result/src/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Header.module.css'; 4 | 5 | const Header = () => ( 6 |

7 | Weather Forecast 8 |

9 | ); 10 | 11 | export default Header; 12 | -------------------------------------------------------------------------------- /starter-files/src/components/UpcomingDaysForecast/UpcomingDaysForecast.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './UpcomingDaysForecast.module.css'; 4 | 5 | const UpcomingDaysForecast = () => ; 6 | 7 | export default UpcomingDaysForecast; 8 | -------------------------------------------------------------------------------- /finished-result/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Weather App", 3 | "name": "The Ultimate API Challenge Weather App", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone" 13 | } -------------------------------------------------------------------------------- /starter-files/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Weather App", 3 | "name": "The Ultimate API Challenge Weather App", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone" 13 | } -------------------------------------------------------------------------------- /finished-result/src/components/UpcomingDaysForecast/UpcomingDaysForecast.module.css: -------------------------------------------------------------------------------- 1 | .weekList { 2 | list-style-type: none; 3 | padding: 0; 4 | -webkit-box-shadow: 0 0 50px -5px rgba(0, 0, 0, 0.25); 5 | box-shadow: 0 0 50px -5px rgba(0, 0, 0, 0.25); 6 | border-radius: var(--secondary-border-radius); 7 | background-color: var(--bg-color--card); 8 | } 9 | -------------------------------------------------------------------------------- /finished-result/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import 'bootstrap/dist/css/bootstrap.min.css'; 5 | import './index.css'; 6 | 7 | import App from './App'; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById('root') 14 | ); 15 | -------------------------------------------------------------------------------- /starter-files/src/components/UpcomingDaysForecast/UpcomingDaysForecast.module.css: -------------------------------------------------------------------------------- 1 | .weekList { 2 | list-style-type: none; 3 | padding: 0; 4 | -webkit-box-shadow: 0 0 50px -5px rgba(0, 0, 0, 0.25); 5 | box-shadow: 0 0 50px -5px rgba(0, 0, 0, 0.25); 6 | border-radius: var(--secondary-border-radius); 7 | background-color: var(--bg-color--card); 8 | } 9 | -------------------------------------------------------------------------------- /starter-files/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import 'bootstrap/dist/css/bootstrap.min.css'; 5 | import './index.css'; 6 | 7 | import App from './App'; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById('root') 14 | ); 15 | -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDayDescriptionItem/CurrentDayDescriptionItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const CurrentDayDescriptionItem = () => ( 4 |
5 |

6 |

7 |
8 | ); 9 | 10 | export default CurrentDayDescriptionItem; 11 | -------------------------------------------------------------------------------- /starter-files/src/components/UpcomingDaysForecastItem/UpcomingDaysForecastItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './UpcomingDaysForecastItem.module.css'; 4 | 5 | const UpcomingDaysForecastItem = () => ( 6 |
  • 7 | ); 8 | 9 | export default UpcomingDaysForecastItem; 10 | -------------------------------------------------------------------------------- /finished-result/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /starter-files/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /finished-result/src/helpers/getUpcomingDaysForecast.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | const getWeekday = date => moment(date).format('dddd').substring(0, 3); 4 | 5 | const getUpcomingDaysForecast = data => 6 | data.slice(1).map(day => ({ 7 | imgUrl: day.weather_state_abbr, 8 | temperature: Math.round(day.max_temp), 9 | weekday: getWeekday(day.applicable_date), 10 | })); 11 | 12 | export default getUpcomingDaysForecast; 13 | -------------------------------------------------------------------------------- /starter-files/src/helpers/getUpcomingDaysForecast.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | const getWeekday = date => moment(date).format('dddd').substring(0, 3); 4 | 5 | const getUpcomingDaysForecast = data => 6 | data.slice(1).map(day => ({ 7 | imgUrl: day.weather_state_abbr, 8 | temperature: Math.round(day.max_temp), 9 | weekday: getWeekday(day.applicable_date), 10 | })); 11 | 12 | export default getUpcomingDaysForecast; 13 | -------------------------------------------------------------------------------- /finished-result/src/components/Forecast/Forecast.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border-radius: var(--main-border-radius); 3 | } 4 | 5 | .box { 6 | border-radius: var(--main-border-radius); 7 | -webkit-box-shadow: 0 0 70px -10px rgba(0, 0, 0, 0.2); 8 | box-shadow: 0 0 70px -10px rgba(0, 0, 0, 0.2); 9 | background-color: var(--bg-color--card); 10 | color: var(--main-text-color); 11 | width: 85%; 12 | max-width: 675px; 13 | min-height: 300px; 14 | } 15 | -------------------------------------------------------------------------------- /starter-files/src/components/Forecast/Forecast.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border-radius: var(--main-border-radius); 3 | } 4 | 5 | .box { 6 | border-radius: var(--main-border-radius); 7 | -webkit-box-shadow: 0 0 70px -10px rgba(0, 0, 0, 0.2); 8 | box-shadow: 0 0 70px -10px rgba(0, 0, 0, 0.2); 9 | background-color: var(--bg-color--card); 10 | color: var(--main-text-color); 11 | width: 85%; 12 | max-width: 675px; 13 | min-height: 300px; 14 | } 15 | -------------------------------------------------------------------------------- /finished-result/src/components/UpcomingDaysForecastItem/UpcomingDaysForecastItem.module.css: -------------------------------------------------------------------------------- 1 | .weekday { 2 | width: 100px; 3 | border-radius: var(--secondary-border-radius); 4 | } 5 | 6 | .weekday:hover { 7 | -webkit-transform: scale(1.1); 8 | -ms-transform: scale(1.1); 9 | transform: scale(1.1); 10 | background: #fff; 11 | color: #222831; 12 | 13 | -webkit-box-shadow: 0 0 40px -5px rgba(0, 0, 0, 0.2); 14 | box-shadow: 0 0 40px -5px rgba(0, 0, 0, 0.2); 15 | } 16 | -------------------------------------------------------------------------------- /starter-files/src/components/UpcomingDaysForecastItem/UpcomingDaysForecastItem.module.css: -------------------------------------------------------------------------------- 1 | .weekday { 2 | width: 100px; 3 | border-radius: var(--secondary-border-radius); 4 | } 5 | 6 | .weekday:hover { 7 | -webkit-transform: scale(1.1); 8 | -ms-transform: scale(1.1); 9 | transform: scale(1.1); 10 | background: #fff; 11 | color: #222831; 12 | 13 | -webkit-box-shadow: 0 0 40px -5px rgba(0, 0, 0, 0.2); 14 | box-shadow: 0 0 40px -5px rgba(0, 0, 0, 0.2); 15 | } 16 | -------------------------------------------------------------------------------- /finished-result/src/components/Page/Page.module.css: -------------------------------------------------------------------------------- 1 | .box { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | margin: 0 auto; 6 | border-radius: var(--main-border-radius); 7 | -webkit-box-shadow: 0 0 24px 7px rgba(0, 0, 0, 0.2); 8 | box-shadow: 0 0 24px 7px rgba(0, 0, 0, 0.2); 9 | background-color: var(--bg-color--card); 10 | color: var(--main-text-color); 11 | width: 85%; 12 | max-width: 675px; 13 | min-height: 300px; 14 | } 15 | -------------------------------------------------------------------------------- /starter-files/src/components/Page/Page.module.css: -------------------------------------------------------------------------------- 1 | .box { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | margin: 0 auto; 6 | border-radius: var(--main-border-radius); 7 | -webkit-box-shadow: 0 0 24px 7px rgba(0, 0, 0, 0.2); 8 | box-shadow: 0 0 24px 7px rgba(0, 0, 0, 0.2); 9 | background-color: var(--bg-color--card); 10 | color: var(--main-text-color); 11 | width: 85%; 12 | max-width: 675px; 13 | min-height: 300px; 14 | } 15 | -------------------------------------------------------------------------------- /starter-files/src/components/Error/Error.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import styles from './Error.module.css'; 5 | 6 | const Error = ({ message }) => ( 7 |
    8 | {message} 9 |
    10 | ); 11 | 12 | Error.propTypes = { 13 | message: PropTypes.string, 14 | }; 15 | 16 | Error.defaultProps = { 17 | message: 'An error occurred', 18 | }; 19 | 20 | export default Error; 21 | -------------------------------------------------------------------------------- /finished-result/src/components/Error/Error.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import styles from './Error.module.css'; 5 | 6 | const Error = ({ message }) => ( 7 |
    8 | {message} 9 |
    10 | ); 11 | 12 | Error.propTypes = { 13 | message: PropTypes.string, 14 | }; 15 | 16 | Error.defaultProps = { 17 | message: 'An error occurred', 18 | }; 19 | 20 | export default Error; 21 | -------------------------------------------------------------------------------- /starter-files/src/helpers/getCurrentDayForecast.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | const getCurrentDayForecast = (data, title) => ({ 4 | weekday: moment(data.applicable_date).format('dddd'), 5 | date: moment(data.applicable_date).format('MMMM Do'), 6 | location: title, 7 | temperature: Math.round(data.the_temp), 8 | weatherIcon: `https://www.metaweather.com/static/img/weather/${data.weather_state_abbr}.svg`, 9 | weatherDescription: data.weather_state_name, 10 | }); 11 | 12 | export default getCurrentDayForecast; 13 | -------------------------------------------------------------------------------- /finished-result/src/helpers/getCurrentDayForecast.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | const getCurrentDayForecast = (data, title) => ({ 4 | weekday: moment(data.applicable_date).format('dddd'), 5 | date: moment(data.applicable_date).format('MMMM Do'), 6 | location: title, 7 | temperature: Math.round(data.the_temp), 8 | weatherIcon: `https://www.metaweather.com/static/img/weather/${data.weather_state_abbr}.svg`, 9 | weatherDescription: data.weather_state_name, 10 | }); 11 | 12 | export default getCurrentDayForecast; 13 | -------------------------------------------------------------------------------- /starter-files/src/components/Forecast/Forecast.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Container, Row, Col } from 'react-bootstrap'; 4 | 5 | import styles from './Forecast.module.css'; 6 | 7 | const Forecast = () => ( 8 | 9 | 10 | 11 |
    12 | 13 | 14 |
    15 |
    16 | ); 17 | 18 | export default Forecast; 19 | -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDay/CurrentDay.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import locationIcon from './assets/location-pin.png'; 4 | import styles from './CurrentDay.module.css'; 5 | 6 | const CurrentDay = () => ( 7 |
    8 |
    9 |
    10 |
    11 |
    12 |
    13 |
    14 |
    15 | ); 16 | 17 | export default CurrentDay; 18 | -------------------------------------------------------------------------------- /finished-result/src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Montserrat:200,300,400,700,900&display=swap'); 2 | 3 | :root { 4 | --gradient: linear-gradient(135deg, #72edf2 10%, #5151e5 100%); 5 | --bg-color: #343d4b; 6 | --bg-color--card: #222831; 7 | 8 | --main-text-color: #ffffff; 9 | 10 | --main-border-radius: 25px; 11 | --secondary-border-radius: 15px; 12 | } 13 | 14 | body { 15 | width: 100%; 16 | height: 100vh; 17 | font-family: 'Montserrat', sans-serif; 18 | background-color: var(--bg-color); 19 | color: var(--main-text-color); 20 | } 21 | -------------------------------------------------------------------------------- /starter-files/src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Montserrat:200,300,400,700,900&display=swap'); 2 | 3 | :root { 4 | --gradient: linear-gradient(135deg, #72edf2 10%, #5151e5 100%); 5 | --bg-color: #343d4b; 6 | --bg-color--card: #222831; 7 | 8 | --main-text-color: #ffffff; 9 | 10 | --main-border-radius: 25px; 11 | --secondary-border-radius: 15px; 12 | } 13 | 14 | body { 15 | width: 100%; 16 | height: 100vh; 17 | font-family: 'Montserrat', sans-serif; 18 | background-color: var(--bg-color); 19 | color: var(--main-text-color); 20 | } 21 | -------------------------------------------------------------------------------- /starter-files/src/components/Form/Form.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Form.module.css'; 4 | 5 | const Form = () => { 6 | return ( 7 |
    8 | 15 | 16 | 19 |
    20 | ); 21 | }; 22 | 23 | export default Form; 24 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDayDescription/CurrentDayDescription.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import CurrentDayDescriptionItem from '../CurrentDayDescriptionItem'; 5 | 6 | const CurrentDayDescription = ({ forecast }) => ( 7 |
    8 |
    9 | {forecast.map(item => ( 10 | 11 | ))} 12 |
    13 |
    14 | ); 15 | 16 | CurrentDayDescription.propTypes = { 17 | forecast: PropTypes.array, 18 | }; 19 | 20 | export default CurrentDayDescription; 21 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDayDescriptionItem/CurrentDayDescriptionItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const CurrentDayDescriptionItem = ({ name, value, unit }) => ( 5 |
    6 |

    {name}

    7 |

    8 | {value} {unit} 9 |

    10 |
    11 | ); 12 | 13 | CurrentDayDescriptionItem.propTypes = { 14 | name: PropTypes.string.isRequired, 15 | value: PropTypes.number.isRequired, 16 | unit: PropTypes.string.isRequired, 17 | }; 18 | 19 | export default CurrentDayDescriptionItem; 20 | -------------------------------------------------------------------------------- /finished-result/src/components/UpcomingDaysForecast/UpcomingDaysForecast.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import UpcomingDaysForecastItem from '../UpcomingDaysForecastItem'; 5 | 6 | import styles from './UpcomingDaysForecast.module.css'; 7 | 8 | const UpcomingDaysForecast = ({ days }) => ( 9 | 14 | ); 15 | 16 | UpcomingDaysForecast.propTypes = { 17 | days: PropTypes.array.isRequired, 18 | }; 19 | 20 | export default UpcomingDaysForecast; 21 | -------------------------------------------------------------------------------- /finished-result/src/components/Form/Form.module.css: -------------------------------------------------------------------------------- 1 | .input { 2 | border-radius: var(--main-border-radius); 3 | margin-bottom: 1rem; 4 | } 5 | 6 | .button { 7 | border: none; 8 | border-radius: var(--main-border-radius); 9 | padding: 0.65rem; 10 | background-image: var(--gradient); 11 | color: var(--main-text-color); 12 | font-weight: 700; 13 | box-shadow: 0 0 30px -5px rgba(0, 0, 0, 0.25); 14 | cursor: pointer; 15 | line-height: initial; 16 | width: 100%; 17 | height: 38px; 18 | } 19 | 20 | .button:hover { 21 | color: var(--main-text-color); 22 | -webkit-transform: scale(0.95); 23 | -ms-transform: scale(0.95); 24 | transform: scale(0.95); 25 | } 26 | -------------------------------------------------------------------------------- /starter-files/src/components/Form/Form.module.css: -------------------------------------------------------------------------------- 1 | .input { 2 | border-radius: var(--main-border-radius); 3 | margin-bottom: 1rem; 4 | } 5 | 6 | .button { 7 | border: none; 8 | border-radius: var(--main-border-radius); 9 | padding: 0.65rem; 10 | background-image: var(--gradient); 11 | color: var(--main-text-color); 12 | font-weight: 700; 13 | box-shadow: 0 0 30px -5px rgba(0, 0, 0, 0.25); 14 | cursor: pointer; 15 | line-height: initial; 16 | width: 100%; 17 | height: 38px; 18 | } 19 | 20 | .button:hover { 21 | color: var(--main-text-color); 22 | -webkit-transform: scale(0.95); 23 | -ms-transform: scale(0.95); 24 | transform: scale(0.95); 25 | } 26 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDay/CurrentDay.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border-radius: var(--main-border-radius); 3 | } 4 | 5 | .cardInner { 6 | z-index: 1; 7 | height: 300px; 8 | } 9 | 10 | .gradient { 11 | position: absolute; 12 | width: 100%; 13 | height: 100%; 14 | top: 0; 15 | left: 0; 16 | background-image: var(--gradient); 17 | background-size: cover; 18 | border-radius: var(--main-border-radius); 19 | opacity: 0.7; 20 | } 21 | 22 | .img { 23 | position: absolute; 24 | width: 100%; 25 | height: 100%; 26 | top: 0; 27 | left: 0; 28 | background-image: url('./assets/mountain.jpg'); 29 | background-size: cover; 30 | border-radius: var(--main-border-radius); 31 | } 32 | 33 | .weekday { 34 | width: 100px; 35 | } 36 | -------------------------------------------------------------------------------- /starter-files/src/components/CurrentDay/CurrentDay.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border-radius: var(--main-border-radius); 3 | } 4 | 5 | .cardInner { 6 | z-index: 1; 7 | height: 300px; 8 | } 9 | 10 | .gradient { 11 | position: absolute; 12 | width: 100%; 13 | height: 100%; 14 | top: 0; 15 | left: 0; 16 | background-image: var(--gradient); 17 | background-size: cover; 18 | border-radius: var(--main-border-radius); 19 | opacity: 0.7; 20 | } 21 | 22 | .img { 23 | position: absolute; 24 | width: 100%; 25 | height: 100%; 26 | top: 0; 27 | left: 0; 28 | background-image: url('./assets/mountain.jpg'); 29 | background-size: cover; 30 | border-radius: var(--main-border-radius); 31 | } 32 | 33 | .weekday { 34 | width: 100px; 35 | } 36 | -------------------------------------------------------------------------------- /starter-files/src/helpers/getCurrentDayDetailedForecast.js: -------------------------------------------------------------------------------- 1 | const currentDayForecast = data => [ 2 | { 3 | name: 'predictability', 4 | value: data.predictability, 5 | unit: '%', 6 | }, 7 | { 8 | name: 'humidity', 9 | value: data.humidity, 10 | unit: '%', 11 | }, 12 | { 13 | name: 'wind', 14 | value: Math.round(data.wind_speed), 15 | unit: 'km/h', 16 | }, 17 | { 18 | name: 'air pressure', 19 | value: data.air_pressure, 20 | unit: 'mb', 21 | }, 22 | { 23 | name: 'max temp', 24 | value: Math.round(data.max_temp), 25 | unit: '°C', 26 | }, 27 | { 28 | name: 'min temp', 29 | value: Math.round(data.min_temp), 30 | unit: '°C', 31 | }, 32 | ]; 33 | 34 | export default currentDayForecast; 35 | -------------------------------------------------------------------------------- /finished-result/src/helpers/getCurrentDayDetailedForecast.js: -------------------------------------------------------------------------------- 1 | const currentDayForecast = data => [ 2 | { 3 | name: 'predictability', 4 | value: data.predictability, 5 | unit: '%', 6 | }, 7 | { 8 | name: 'humidity', 9 | value: data.humidity, 10 | unit: '%', 11 | }, 12 | { 13 | name: 'wind', 14 | value: Math.round(data.wind_speed), 15 | unit: 'km/h', 16 | }, 17 | { 18 | name: 'air pressure', 19 | value: data.air_pressure, 20 | unit: 'mb', 21 | }, 22 | { 23 | name: 'max temp', 24 | value: Math.round(data.max_temp), 25 | unit: '°C', 26 | }, 27 | { 28 | name: 'min temp', 29 | value: Math.round(data.min_temp), 30 | unit: '°C', 31 | }, 32 | ]; 33 | 34 | export default currentDayForecast; 35 | -------------------------------------------------------------------------------- /finished-result/src/components/UpcomingDaysForecastItem/UpcomingDaysForecastItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import styles from './UpcomingDaysForecastItem.module.css'; 5 | 6 | const imgUrlBase = 'https://www.metaweather.com/static/'; 7 | 8 | const UpcomingDaysForecastItem = ({ weekday, temperature, imgUrl }) => ( 9 |
  • 10 | 11 | {weekday} 12 | {temperature}° 13 |
  • 14 | ); 15 | 16 | UpcomingDaysForecastItem.propTypes = { 17 | weekday: PropTypes.string.isRequired, 18 | temperature: PropTypes.string.isRequired, 19 | imgUrl: PropTypes.string.isRequired, 20 | }; 21 | 22 | export default UpcomingDaysForecastItem; 23 | -------------------------------------------------------------------------------- /finished-result/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "the-ultimate-api-challenge-weather-app-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "axios": "^0.21.1", 10 | "bootstrap": "^4.6.0", 11 | "moment": "^2.29.1", 12 | "react": "^17.0.2", 13 | "react-bootstrap": "^1.6.0", 14 | "react-dom": "^17.0.2", 15 | "react-scripts": "^4.0.3", 16 | "web-vitals": "^1.0.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } -------------------------------------------------------------------------------- /starter-files/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "the-ultimate-api-challenge-weather-app-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "axios": "^0.21.1", 10 | "bootstrap": "^4.6.0", 11 | "moment": "^2.29.1", 12 | "react": "^17.0.2", 13 | "react-bootstrap": "^1.6.0", 14 | "react-dom": "^17.0.2", 15 | "react-scripts": "^4.0.3", 16 | "web-vitals": "^1.0.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } -------------------------------------------------------------------------------- /finished-result/src/components/Form/Form.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import styles from './Form.module.css'; 5 | 6 | const Form = ({ submitSearch }) => { 7 | const [location, setLocation] = useState(''); 8 | 9 | const onSubmit = e => { 10 | e.preventDefault(); 11 | if (!location || location === '') return; 12 | submitSearch(location); 13 | }; 14 | 15 | return ( 16 |
    17 | setLocation(e.target.value)} 25 | /> 26 | 27 | 30 |
    31 | ); 32 | }; 33 | 34 | Form.propTypes = { 35 | submitSearch: PropTypes.func.isRequired, 36 | }; 37 | 38 | export default Form; 39 | -------------------------------------------------------------------------------- /finished-result/src/components/Page/Page.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | 3 | import Header from '../Header'; 4 | import Form from '../Form'; 5 | import Error from '../Error'; 6 | import Loader from '../Loader'; 7 | import Forecast from '../Forecast'; 8 | 9 | import useForecast from '../../hooks/useForecast'; 10 | 11 | import styles from './Page.module.css'; 12 | 13 | const Page = () => { 14 | const { isError, isLoading, forecast, submitRequest } = useForecast(); 15 | 16 | const onSubmit = value => { 17 | submitRequest(value); 18 | }; 19 | 20 | return ( 21 | 22 |
    23 | {!forecast && ( 24 |
    25 | {/* Form */} 26 | {!isLoading &&
    } 27 | {/* Error */} 28 | {isError && } 29 | {/* Loader */} 30 | {isLoading && } 31 |
    32 | )} 33 | {/* Forecast */} 34 | {forecast && } 35 | 36 | ); 37 | }; 38 | 39 | export default Page; 40 | -------------------------------------------------------------------------------- /finished-result/src/components/Forecast/Forecast.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { Container, Row, Col } from 'react-bootstrap'; 5 | 6 | import CurrentDay from '../CurrentDay'; 7 | import CurrentDayDescription from '../CurrentDayDescription'; 8 | import UpcomingDaysForecast from '../UpcomingDaysForecast'; 9 | 10 | import styles from './Forecast.module.css'; 11 | 12 | const Forecast = ({ forecast }) => ( 13 | 14 | 15 | 16 |
    17 | 18 |
    19 | 20 | 21 | 22 | 23 | 24 |
    25 |
    26 | ); 27 | 28 | Forecast.propTypes = { 29 | forecast: PropTypes.shape({ 30 | currentDay: PropTypes.object, 31 | currentDayDetails: PropTypes.array, 32 | upcomingDays: PropTypes.array, 33 | }), 34 | }; 35 | 36 | export default Forecast; 37 | -------------------------------------------------------------------------------- /finished-result/src/components/CurrentDay/CurrentDay.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import locationIcon from './assets/location-pin.png'; 5 | import styles from './CurrentDay.module.css'; 6 | 7 | const CurrentDay = ({ weekday, date, location, temperature, weatherIcon, weatherDescription }) => ( 8 |
    9 |
    10 |
    11 |
    12 |
    13 |

    {weekday}

    14 |

    {date}

    15 |

    16 | location pin icon 17 | {location} 18 |

    19 |
    20 |
    21 | 22 |

    23 | {temperature}°C 24 |

    25 |
    {weatherDescription}
    26 |
    27 |
    28 |
    29 | ); 30 | 31 | CurrentDay.propTypes = { 32 | weekday: PropTypes.string.isRequired, 33 | date: PropTypes.string.isRequired, 34 | location: PropTypes.string.isRequired, 35 | weatherIcon: PropTypes.string.isRequired, 36 | temperature: PropTypes.number.isRequired, 37 | weatherDescription: PropTypes.string.isRequired, 38 | }; 39 | 40 | export default CurrentDay; 41 | -------------------------------------------------------------------------------- /starter-files/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 16 | 17 | 26 | Weather App 27 | 28 | 29 | 30 |
    31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /finished-result/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 16 | 17 | 26 | Weather App 27 | 28 | 29 | 30 |
    31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /starter-files/src/components/Loader/Loader.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Loader.module.css'; 4 | 5 | const Loader = () => ( 6 |
    7 | 17 | 18 | 27 | 28 | 29 | 38 | 39 | 40 | 49 | 50 | 51 |
    52 | ); 53 | 54 | export default Loader; 55 | -------------------------------------------------------------------------------- /finished-result/src/components/Loader/Loader.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Loader.module.css'; 4 | 5 | const Loader = () => ( 6 |
    7 | 17 | 18 | 27 | 28 | 29 | 38 | 39 | 40 | 49 | 50 | 51 |
    52 | ); 53 | 54 | export default Loader; 55 | -------------------------------------------------------------------------------- /finished-result/src/hooks/useForecast.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import axios from 'axios'; 3 | 4 | import getCurrentDayForecast from '../helpers/getCurrentDayForecast'; 5 | import getCurrentDayDetailedForecast from '../helpers/getCurrentDayDetailedForecast'; 6 | import getUpcomingDaysForecast from '../helpers/getUpcomingDaysForecast'; 7 | 8 | const BASE_URL = 'https://www.metaweather.com/api/location'; 9 | const CROSS_DOMAIN = 'https://the-ultimate-api-challenge.herokuapp.com'; 10 | const REQUEST_URL = `${CROSS_DOMAIN}/${BASE_URL}`; 11 | 12 | const useForecast = () => { 13 | const [isError, setError] = useState(false); 14 | const [isLoading, setLoading] = useState(false); 15 | const [forecast, setForecast] = useState(null); 16 | 17 | const getWoeid = async location => { 18 | const { data } = await axios(`${REQUEST_URL}/search`, { params: { query: location } }); 19 | 20 | if (!data || data.length === 0) { 21 | setError('There is no such location'); 22 | setLoading(false); 23 | return; 24 | } 25 | 26 | return data[0]; 27 | }; 28 | 29 | const getForecastData = async woeid => { 30 | const { data } = await axios(`${REQUEST_URL}/${woeid}`); 31 | 32 | if (!data || data.length === 0) { 33 | setError('Something went wrong'); 34 | setLoading(false); 35 | return; 36 | } 37 | 38 | return data; 39 | }; 40 | 41 | const gatherForecastData = data => { 42 | const currentDay = getCurrentDayForecast(data.consolidated_weather[0], data.title); 43 | const currentDayDetails = getCurrentDayDetailedForecast(data.consolidated_weather[0]); 44 | const upcomingDays = getUpcomingDaysForecast(data.consolidated_weather); 45 | 46 | setForecast({ currentDay, currentDayDetails, upcomingDays }); 47 | setLoading(false); 48 | }; 49 | 50 | const submitRequest = async location => { 51 | setLoading(true); 52 | setError(false); 53 | 54 | const response = await getWoeid(location); 55 | if (!response?.woeid) return; 56 | 57 | const data = await getForecastData(response.woeid); 58 | if (!data) return; 59 | 60 | gatherForecastData(data); 61 | }; 62 | 63 | return { 64 | isError, 65 | isLoading, 66 | forecast, 67 | submitRequest, 68 | }; 69 | }; 70 | 71 | export default useForecast; 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Weather API - ReactJS Version 2 | 3 | --- 4 | 5 |
    6 | Quick menu: 7 | Challenge Page | 14 | Official API Docs | 21 | YouTube Video 28 |
    29 | 30 | --- 31 | 32 | ## Description 33 | 34 |

    Hands down, the most popular API project for beginners must be the weather app API. We will build the weather app using javascript and ReactJS hooks, async/await as well as AXIOS and CSS modules!

    35 | 36 |

    The ReactJS Weather API Challenge will not only include working with more complex response, but it comes with a twist. Often times APIs aren't perfectly "fitting" into what the interface expects, so this challenge took a bit of a different direction and is about making some data manipulation before showing it to the user! And it's my ultimate favourite part about it!

    37 | 38 |

    You will learn how to use Hooks in ReactJS, how to create your own custom ones too, we refactor some functions and check that our code doesn't break depending on the response we're getting from the API. It's gonna be fun and challenging, but don't give up!

    39 | 40 |

    Feel free to try out first yourself or jump on the YouTube video from the start, find what works best fo you and enjoy! 🐣 41 |

    42 | 43 | ### Tech stack 44 | 45 | ``` 46 | Core: 47 | - CSS Modules 48 | - React.js 49 | - Hooks 50 | 51 | Communication tool: 52 | - axios 53 | ``` 54 | 55 | ### Prerequisites 56 | 57 | ``` 58 | Knowledge level: 59 | basic understanding of HTML, CSS and JavaScript 60 | 61 | Tech: 62 | a computer/laptop to copy the files to with a code editor of your choice 63 | ``` 64 | 65 | ## Preview 66 | 67 | 68 | --------------------------------------------------------------------------------