├── part1
├── lecture-01
│ ├── style.css
│ ├── main.js
│ └── index.html
├── challenge-02
│ ├── final
│ │ ├── style.css
│ │ ├── index.html
│ │ └── main.js
│ └── starter
│ │ ├── style.css
│ │ ├── index.html
│ │ └── main.js
├── lecture-02
│ ├── style.css
│ ├── index.html
│ └── main.js
├── lecture-03
│ ├── style.css
│ ├── index.html
│ └── main.js
├── lecture-04
│ ├── final
│ │ ├── style.css
│ │ ├── index.html
│ │ └── main.js
│ └── starter
│ │ ├── style.css
│ │ ├── index.html
│ │ └── main.js
└── challenge-01
│ ├── final
│ ├── style.css
│ ├── index.html
│ └── main.js
│ └── start
│ ├── style.css
│ ├── index.html
│ └── main.js
├── .gitignore
├── part2
├── weather-app
│ ├── final-01
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── Home.module.css
│ │ │ │ ├── Container.module.css
│ │ │ │ ├── Container.jsx
│ │ │ │ ├── Home.jsx
│ │ │ │ ├── Day.jsx
│ │ │ │ └── Forecast.jsx
│ │ │ ├── App.jsx
│ │ │ ├── index.css
│ │ │ ├── main.jsx
│ │ │ └── hooks
│ │ │ │ └── useGeolocation.js
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ ├── final-02
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── Home.module.css
│ │ │ │ ├── Container.module.css
│ │ │ │ ├── Container.jsx
│ │ │ │ ├── Day.jsx
│ │ │ │ ├── Home.jsx
│ │ │ │ └── Forecast.jsx
│ │ │ ├── App.jsx
│ │ │ ├── index.css
│ │ │ ├── services
│ │ │ │ └── apiWeather.js
│ │ │ ├── main.jsx
│ │ │ └── hooks
│ │ │ │ └── useGeolocation.js
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ ├── final-03
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── Home.module.css
│ │ │ │ ├── Container.module.css
│ │ │ │ ├── Container.jsx
│ │ │ │ ├── Home.jsx
│ │ │ │ ├── Day.jsx
│ │ │ │ └── Forecast.jsx
│ │ │ ├── App.jsx
│ │ │ ├── index.css
│ │ │ ├── services
│ │ │ │ └── apiWeather.js
│ │ │ ├── main.jsx
│ │ │ └── hooks
│ │ │ │ ├── useGeolocation.js
│ │ │ │ └── useCurrentWeather.js
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ ├── final-04
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── Home.module.css
│ │ │ │ ├── Container.module.css
│ │ │ │ ├── Container.jsx
│ │ │ │ ├── Day.jsx
│ │ │ │ ├── Home.jsx
│ │ │ │ └── Forecast.jsx
│ │ │ ├── index.css
│ │ │ ├── services
│ │ │ │ └── apiWeather.js
│ │ │ ├── main.jsx
│ │ │ ├── App.jsx
│ │ │ └── hooks
│ │ │ │ ├── useGeolocation.js
│ │ │ │ └── useCurrentWeather.js
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ ├── final-05
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── Home.module.css
│ │ │ │ ├── Container.module.css
│ │ │ │ ├── Loading.jsx
│ │ │ │ ├── Container.jsx
│ │ │ │ ├── Day.jsx
│ │ │ │ ├── Home.jsx
│ │ │ │ └── Forecast.jsx
│ │ │ ├── index.css
│ │ │ ├── main.jsx
│ │ │ ├── services
│ │ │ │ └── apiWeather.js
│ │ │ ├── App.jsx
│ │ │ └── hooks
│ │ │ │ ├── useCurrentWeather.js
│ │ │ │ └── useGeolocation.js
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ ├── final-06
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── Home.module.css
│ │ │ │ ├── Container.module.css
│ │ │ │ ├── Loading.jsx
│ │ │ │ ├── Container.jsx
│ │ │ │ ├── Day.jsx
│ │ │ │ ├── Home.jsx
│ │ │ │ └── Forecast.jsx
│ │ │ ├── index.css
│ │ │ ├── main.jsx
│ │ │ ├── services
│ │ │ │ └── apiWeather.js
│ │ │ ├── App.jsx
│ │ │ └── hooks
│ │ │ │ ├── useCurrentWeather.js
│ │ │ │ └── useGeolocation.js
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ ├── final-07
│ │ ├── src
│ │ │ ├── features
│ │ │ │ ├── home
│ │ │ │ │ ├── Home.module.css
│ │ │ │ │ ├── Day.jsx
│ │ │ │ │ └── Home.jsx
│ │ │ │ └── forecast
│ │ │ │ │ ├── AppNav.jsx
│ │ │ │ │ ├── Forecast.jsx
│ │ │ │ │ └── ForecastList.jsx
│ │ │ ├── ui
│ │ │ │ ├── Container.module.css
│ │ │ │ ├── Loading.jsx
│ │ │ │ └── Container.jsx
│ │ │ ├── index.css
│ │ │ ├── main.jsx
│ │ │ ├── services
│ │ │ │ └── apiWeather.js
│ │ │ ├── App.jsx
│ │ │ └── hooks
│ │ │ │ ├── useCurrentWeather.js
│ │ │ │ ├── useGeolocation.js
│ │ │ │ └── useForecastWeather.js
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ └── starter
│ │ ├── src
│ │ ├── components
│ │ │ ├── Home.module.css
│ │ │ ├── Container.module.css
│ │ │ ├── Container.jsx
│ │ │ ├── Home.jsx
│ │ │ ├── Day.jsx
│ │ │ └── Forecast.jsx
│ │ ├── App.jsx
│ │ ├── index.css
│ │ └── main.jsx
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ └── vite.svg
├── lecture-03
│ ├── final
│ │ ├── vite.config.js
│ │ ├── src
│ │ │ ├── App.css
│ │ │ ├── main.jsx
│ │ │ ├── App.jsx
│ │ │ └── assets
│ │ │ │ └── react.svg
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ └── starter
│ │ ├── style.css
│ │ ├── index.html
│ │ └── main.js
├── lecture-04
│ ├── final
│ │ ├── vite.config.js
│ │ ├── src
│ │ │ ├── App.css
│ │ │ ├── main.jsx
│ │ │ ├── UsernameInput.jsx
│ │ │ └── App.jsx
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ └── starter
│ │ ├── vite.config.js
│ │ ├── src
│ │ ├── App.css
│ │ ├── main.jsx
│ │ └── App.jsx
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ └── vite.svg
├── lecture-05
│ ├── final
│ │ ├── vite.config.js
│ │ ├── src
│ │ │ ├── App.css
│ │ │ ├── main.jsx
│ │ │ ├── AppInput.jsx
│ │ │ └── App.jsx
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ └── starter
│ │ ├── vite.config.js
│ │ ├── src
│ │ ├── App.css
│ │ ├── main.jsx
│ │ ├── UsernameInput.jsx
│ │ ├── PasswordInput.jsx
│ │ └── App.jsx
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ └── vite.svg
├── lecture-06
│ ├── final
│ │ ├── vite.config.js
│ │ ├── src
│ │ │ ├── AppButton.jsx
│ │ │ ├── App.css
│ │ │ ├── main.jsx
│ │ │ ├── AppInput.jsx
│ │ │ └── App.jsx
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ │ └── vite.svg
│ └── starter
│ │ ├── vite.config.js
│ │ ├── src
│ │ ├── App.css
│ │ ├── main.jsx
│ │ ├── AppInput.jsx
│ │ └── App.jsx
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ └── vite.svg
├── lecture-07
│ └── final
│ │ ├── vite.config.js
│ │ ├── src
│ │ ├── main.jsx
│ │ ├── App.jsx
│ │ └── App.css
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ └── vite.svg
├── lecture-08
│ └── final
│ │ ├── vite.config.js
│ │ ├── src
│ │ ├── main.jsx
│ │ ├── App.jsx
│ │ └── App.css
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ └── vite.svg
├── lecture-09
│ └── final
│ │ ├── vite.config.js
│ │ ├── src
│ │ ├── main.jsx
│ │ ├── App.css
│ │ └── App.jsx
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ └── vite.svg
├── lecture-10
│ └── final
│ │ ├── vite.config.js
│ │ ├── src
│ │ ├── Time.jsx
│ │ ├── App.jsx
│ │ ├── main.jsx
│ │ ├── Advice.jsx
│ │ ├── useTime.js
│ │ ├── useAdvice.js
│ │ └── App.css
│ │ ├── index.html
│ │ ├── package.json
│ │ └── public
│ │ └── vite.svg
└── challenge-03
│ ├── final
│ ├── vite.config.js
│ ├── src
│ │ ├── main.jsx
│ │ ├── AppInput.jsx
│ │ ├── App.css
│ │ ├── App.jsx
│ │ └── assets
│ │ │ └── react.svg
│ ├── index.html
│ ├── package.json
│ └── public
│ │ └── vite.svg
│ └── starter
│ ├── style.css
│ ├── index.html
│ └── main.js
├── README.md
└── LICENSE
/part1/lecture-01/style.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Exclude node_modules
2 | */**/node_modules
3 | */**/pnpm-lock.yaml
4 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/components/Home.module.css:
--------------------------------------------------------------------------------
1 | section {
2 | margin-top: 40%;
3 | }
4 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/components/Home.module.css:
--------------------------------------------------------------------------------
1 | section {
2 | margin-top: 40%;
3 | }
4 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/components/Home.module.css:
--------------------------------------------------------------------------------
1 | section {
2 | margin-top: 40%;
3 | }
4 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/components/Home.module.css:
--------------------------------------------------------------------------------
1 | section {
2 | margin-top: 40%;
3 | }
4 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/components/Home.module.css:
--------------------------------------------------------------------------------
1 | section {
2 | margin-top: 40%;
3 | }
4 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/components/Home.module.css:
--------------------------------------------------------------------------------
1 | section {
2 | margin-top: 40%;
3 | }
4 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/features/home/Home.module.css:
--------------------------------------------------------------------------------
1 | section {
2 | margin-top: 40%;
3 | }
4 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/components/Home.module.css:
--------------------------------------------------------------------------------
1 | section {
2 | margin-top: 40%;
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This tutorial materials base on:
2 |
3 | - [bilibili](https://www.bilibili.com/video/BV1qK421x79b/)
4 | - [西瓜视频](https://www.ixigua.com/7352100939946033674)
5 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/ui/Container.module.css:
--------------------------------------------------------------------------------
1 | main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/components/Container.module.css:
--------------------------------------------------------------------------------
1 | main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/components/Container.module.css:
--------------------------------------------------------------------------------
1 | main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/components/Container.module.css:
--------------------------------------------------------------------------------
1 | main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/components/Container.module.css:
--------------------------------------------------------------------------------
1 | main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/components/Container.module.css:
--------------------------------------------------------------------------------
1 | main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/components/Container.module.css:
--------------------------------------------------------------------------------
1 | main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/components/Container.module.css:
--------------------------------------------------------------------------------
1 | main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
--------------------------------------------------------------------------------
/part1/challenge-02/final/style.css:
--------------------------------------------------------------------------------
1 | main {
2 | text-align: center;
3 | }
4 |
5 | li {
6 | list-style: none;
7 | }
8 |
9 | .deleted {
10 | text-decoration: line-through;
11 | }
12 |
--------------------------------------------------------------------------------
/part1/challenge-02/starter/style.css:
--------------------------------------------------------------------------------
1 | main {
2 | text-align: center;
3 | }
4 |
5 | li {
6 | list-style: none;
7 | }
8 |
9 | .deleted {
10 | text-decoration: line-through;
11 | }
12 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/ui/Loading.jsx:
--------------------------------------------------------------------------------
1 | import { CircularProgress } from "@mui/material";
2 |
3 | function Loading() {
4 | return ;
5 | }
6 |
7 | export default Loading;
8 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/components/Loading.jsx:
--------------------------------------------------------------------------------
1 | import { CircularProgress } from "@mui/material";
2 |
3 | function Loading() {
4 | return ;
5 | }
6 | export default Loading;
7 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/components/Loading.jsx:
--------------------------------------------------------------------------------
1 | import { CircularProgress } from "@mui/material";
2 |
3 | function Loading() {
4 | return ;
5 | }
6 | export default Loading;
7 |
--------------------------------------------------------------------------------
/part1/lecture-02/style.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .btn {
6 | margin: 1rem;
7 | }
8 |
9 | h2 {
10 | text-align: center;
11 | }
12 |
13 | main {
14 | border: 1px solid black;
15 | }
16 |
--------------------------------------------------------------------------------
/part2/lecture-03/final/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 |
--------------------------------------------------------------------------------
/part2/lecture-04/final/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 |
--------------------------------------------------------------------------------
/part2/lecture-05/final/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 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/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 |
--------------------------------------------------------------------------------
/part2/lecture-07/final/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 |
--------------------------------------------------------------------------------
/part2/lecture-08/final/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 |
--------------------------------------------------------------------------------
/part2/lecture-09/final/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 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/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 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/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 |
--------------------------------------------------------------------------------
/part2/lecture-04/starter/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 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/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 |
--------------------------------------------------------------------------------
/part2/lecture-06/starter/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 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/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 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/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 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/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 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/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 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/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 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/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 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/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 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/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 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/src/Time.jsx:
--------------------------------------------------------------------------------
1 | import { useTime } from "./useTime";
2 |
3 | function Time() {
4 | const { currentTime } = useTime();
5 |
6 | return
{currentTime}
;
7 | }
8 |
9 | export default Time;
10 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/src/AppButton.jsx:
--------------------------------------------------------------------------------
1 | function AppButton({ children }) {
2 | return (
3 |
6 | );
7 | }
8 |
9 | export default AppButton;
10 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/ui/Container.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Container.module.css";
2 |
3 | function Container({ children }) {
4 | return {children};
5 | }
6 |
7 | export default Container;
8 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Container.module.css";
2 |
3 | function Container({ children }) {
4 | return {children};
5 | }
6 |
7 | export default Container;
8 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Container.module.css";
2 |
3 | function Container({ children }) {
4 | return {children};
5 | }
6 |
7 | export default Container;
8 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Container.module.css";
2 |
3 | function Container({ children }) {
4 | return {children};
5 | }
6 |
7 | export default Container;
8 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Container.module.css";
2 |
3 | function Container({ children }) {
4 | return {children};
5 | }
6 |
7 | export default Container;
8 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Container.module.css";
2 |
3 | function Container({ children }) {
4 | return {children};
5 | }
6 |
7 | export default Container;
8 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Container.module.css";
2 |
3 | function Container({ children }) {
4 | return {children};
5 | }
6 |
7 | export default Container;
8 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Container.module.css";
2 |
3 | function Container({ children }) {
4 | return {children};
5 | }
6 |
7 | export default Container;
8 |
--------------------------------------------------------------------------------
/part1/lecture-03/style.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-03/final/src/App.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-03/starter/style.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-04/final/src/App.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-05/final/src/App.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/src/App.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import Time from "./Time";
2 | import Advice from "./Advice";
3 |
4 | function App() {
5 | return (
6 | <>
7 | Advice
8 |
9 |
10 | >
11 | );
12 | }
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/part2/lecture-03/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-04/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-04/starter/src/App.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-05/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/src/App.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-06/starter/src/App.css:
--------------------------------------------------------------------------------
1 | .input {
2 | margin: 2px;
3 | }
4 |
5 | .input-error {
6 | border: 1px solid red;
7 | }
8 |
9 | .btn {
10 | margin: 1rem;
11 | }
12 |
13 | h2 {
14 | text-align: center;
15 | }
16 |
17 | main {
18 | border: 1px solid black;
19 | }
20 |
--------------------------------------------------------------------------------
/part2/lecture-07/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-08/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-09/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-04/starter/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part2/lecture-06/starter/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")).render(
6 |
7 |
8 |
9 | );
10 |
--------------------------------------------------------------------------------
/part1/lecture-04/final/style.css:
--------------------------------------------------------------------------------
1 | .city {
2 | text-align: center;
3 | border: 1px solid orangered;
4 | border-radius: 4px;
5 | margin: 8px auto;
6 | }
7 |
8 | li {
9 | list-style: none;
10 | }
11 |
12 | h2 {
13 | color: blueviolet;
14 | }
15 |
16 | h3 {
17 | color: brown;
18 | }
19 |
--------------------------------------------------------------------------------
/part1/lecture-04/starter/style.css:
--------------------------------------------------------------------------------
1 | .city {
2 | text-align: center;
3 | border: 1px solid orangered;
4 | border-radius: 4px;
5 | margin: 8px auto;
6 | }
7 |
8 | li {
9 | list-style: none;
10 | }
11 |
12 | h2 {
13 | color: blueviolet;
14 | }
15 |
16 | h3 {
17 | color: brown;
18 | }
19 |
--------------------------------------------------------------------------------
/part2/lecture-04/final/src/UsernameInput.jsx:
--------------------------------------------------------------------------------
1 | function UsernameInput({ className, value, onChange }) {
2 | return (
3 |
9 | );
10 | }
11 |
12 | export default UsernameInput;
13 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/src/UsernameInput.jsx:
--------------------------------------------------------------------------------
1 | function UsernameInput({ className, value, onChange }) {
2 | return (
3 |
9 | );
10 | }
11 |
12 | export default UsernameInput;
13 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/src/PasswordInput.jsx:
--------------------------------------------------------------------------------
1 | function PasswordInput({ className, value, onChange }) {
2 | return (
3 |
9 | );
10 | }
11 |
12 | export default PasswordInput;
13 |
--------------------------------------------------------------------------------
/part2/lecture-05/final/src/AppInput.jsx:
--------------------------------------------------------------------------------
1 | function AppInput({ className, type, value, setState }) {
2 | return (
3 | setState(event.target.value)}
8 | />
9 | );
10 | }
11 |
12 | export default AppInput;
13 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/src/AppInput.jsx:
--------------------------------------------------------------------------------
1 | function AppInput({ className, type, value, setState }) {
2 | return (
3 | setState(event.target.value)}
8 | />
9 | );
10 | }
11 |
12 | export default AppInput;
13 |
--------------------------------------------------------------------------------
/part2/lecture-06/starter/src/AppInput.jsx:
--------------------------------------------------------------------------------
1 | function AppInput({ className, type, value, setState }) {
2 | return (
3 | setState(event.target.value)}
8 | />
9 | );
10 | }
11 |
12 | export default AppInput;
13 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/App.jsx:
--------------------------------------------------------------------------------
1 | import Container from "./components/Container";
2 | import Forecast from "./components/Forecast";
3 | import Home from "./components/Home";
4 |
5 | function App() {
6 | return (
7 |
8 |
9 | {/* */}
10 |
11 | );
12 | }
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/App.jsx:
--------------------------------------------------------------------------------
1 | import Container from "./components/Container";
2 | import Forecast from "./components/Forecast";
3 | import Home from "./components/Home";
4 |
5 | function App() {
6 | return (
7 |
8 |
9 | {/* */}
10 |
11 | );
12 | }
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/App.jsx:
--------------------------------------------------------------------------------
1 | import Container from "./components/Container";
2 | import Forecast from "./components/Forecast";
3 | import Home from "./components/Home";
4 |
5 | function App() {
6 | return (
7 |
8 |
9 | {/* */}
10 |
11 | );
12 | }
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/App.jsx:
--------------------------------------------------------------------------------
1 | import Container from "./components/Container";
2 | import Forecast from "./components/Forecast";
3 | import Home from "./components/Home";
4 |
5 | function App() {
6 | return (
7 |
8 |
9 | {/* */}
10 |
11 | );
12 | }
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/src/AppInput.jsx:
--------------------------------------------------------------------------------
1 | function AppInput({ value, setState, children }) {
2 | return (
3 |
11 | );
12 | }
13 |
14 | export default AppInput;
15 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/src/Advice.jsx:
--------------------------------------------------------------------------------
1 | import { useAdvice } from "./useAdvice";
2 |
3 | function Advice() {
4 | const { isLoading, advice, getAdvice } = useAdvice();
5 |
6 | return (
7 | <>
8 | {isLoading ? "Loading..." : advice}
9 |
10 | >
11 | );
12 | }
13 |
14 | export default Advice;
15 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/index.css:
--------------------------------------------------------------------------------
1 | main {
2 | margin: 0 auto;
3 | text-align: center;
4 | }
5 |
6 | .day {
7 | padding: 1.6rem 0 2rem;
8 | width: 12.8rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | gap: 1.2rem;
14 |
15 | /* clickable */
16 | cursor: pointer;
17 | }
18 |
19 | .day img {
20 | font-size: 5.2rem;
21 | }
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/index.css:
--------------------------------------------------------------------------------
1 | main {
2 | margin: 0 auto;
3 | text-align: center;
4 | }
5 |
6 | .day {
7 | padding: 1.6rem 0 2rem;
8 | width: 12.8rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | gap: 1.2rem;
14 |
15 | /* clickable */
16 | cursor: pointer;
17 | }
18 |
19 | .day img {
20 | font-size: 5.2rem;
21 | }
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/index.css:
--------------------------------------------------------------------------------
1 | main {
2 | margin: 0 auto;
3 | text-align: center;
4 | }
5 |
6 | .day {
7 | padding: 1.6rem 0 2rem;
8 | width: 12.8rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | gap: 1.2rem;
14 |
15 | /* clickable */
16 | cursor: pointer;
17 | }
18 |
19 | .day img {
20 | font-size: 5.2rem;
21 | }
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/index.css:
--------------------------------------------------------------------------------
1 | main {
2 | margin: 0 auto;
3 | text-align: center;
4 | }
5 |
6 | .day {
7 | padding: 1.6rem 0 2rem;
8 | width: 12.8rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | gap: 1.2rem;
14 |
15 | /* clickable */
16 | cursor: pointer;
17 | }
18 |
19 | .day img {
20 | font-size: 5.2rem;
21 | }
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/index.css:
--------------------------------------------------------------------------------
1 | main {
2 | margin: 0 auto;
3 | text-align: center;
4 | }
5 |
6 | .day {
7 | padding: 1.6rem 0 2rem;
8 | width: 12.8rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | gap: 1.2rem;
14 |
15 | /* clickable */
16 | cursor: pointer;
17 | }
18 |
19 | .day img {
20 | font-size: 5.2rem;
21 | }
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/index.css:
--------------------------------------------------------------------------------
1 | main {
2 | margin: 0 auto;
3 | text-align: center;
4 | }
5 |
6 | .day {
7 | padding: 1.6rem 0 2rem;
8 | width: 12.8rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | gap: 1.2rem;
14 |
15 | /* clickable */
16 | cursor: pointer;
17 | }
18 |
19 | .day img {
20 | font-size: 5.2rem;
21 | }
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/index.css:
--------------------------------------------------------------------------------
1 | main {
2 | margin: 0 auto;
3 | text-align: center;
4 | }
5 |
6 | .day {
7 | padding: 1.6rem 0 2rem;
8 | width: 12.8rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | gap: 1.2rem;
14 |
15 | /* clickable */
16 | cursor: pointer;
17 | }
18 |
19 | .day img {
20 | font-size: 5.2rem;
21 | }
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/index.css:
--------------------------------------------------------------------------------
1 | main {
2 | margin: 0 auto;
3 | text-align: center;
4 | }
5 |
6 | .day {
7 | padding: 1.6rem 0 2rem;
8 | width: 12.8rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | gap: 1.2rem;
14 |
15 | /* clickable */
16 | cursor: pointer;
17 | }
18 |
19 | .day img {
20 | font-size: 5.2rem;
21 | }
22 |
--------------------------------------------------------------------------------
/part1/lecture-01/main.js:
--------------------------------------------------------------------------------
1 | function AppContent() {
2 | const [text, setText] = React.useState("");
3 |
4 | return (
5 |
6 | setText(event.target.value)}
8 | />
9 | {text}
10 |
11 | );
12 | }
13 |
14 | const appEl = document.querySelector("#app");
15 | const root = ReactDOM.createRoot(appEl);
16 |
17 | root.render();
18 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@mui/material";
2 | import Day from "./Day";
3 | import styles from "./Home.module.css";
4 |
5 | function Home() {
6 | return (
7 |
8 |
9 |
12 |
13 | );
14 | }
15 | export default Home;
16 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-03/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-04/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-04/starter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-05/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-06/starter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-07/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-08/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-09/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/services/apiWeather.js:
--------------------------------------------------------------------------------
1 | const API_KEY = "8044c5ff43da29b1a91b760bf3a91592";
2 |
3 | export async function getCurrentWeather(lat, lon) {
4 | try {
5 | const response = await fetch(
6 | `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
7 | );
8 |
9 | return await response.json();
10 | } catch (error) {
11 | throw new Error(error.message);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/services/apiWeather.js:
--------------------------------------------------------------------------------
1 | const API_KEY = "8044c5ff43da29b1a91b760bf3a91592";
2 |
3 | export async function getCurrentWeather(lat, lon) {
4 | try {
5 | const response = await fetch(
6 | `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
7 | );
8 |
9 | return await response.json();
10 | } catch (error) {
11 | throw new Error(error.message);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/services/apiWeather.js:
--------------------------------------------------------------------------------
1 | const API_KEY = "8044c5ff43da29b1a91b760bf3a91592";
2 |
3 | export async function getCurrentWeather(lat, lon) {
4 | try {
5 | const response = await fetch(
6 | `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
7 | );
8 |
9 | return await response.json();
10 | } catch (error) {
11 | throw new Error(error.message);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/src/useTime.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | export function useTime() {
4 | const [currentTime, setCurrentTime] = useState(new Date().toLocaleString());
5 |
6 | useEffect(() => {
7 | const interval = setInterval(() => {
8 | setCurrentTime(new Date().toLocaleString());
9 | }, 1000);
10 |
11 | return () => {
12 | clearInterval(interval);
13 | };
14 | }, []);
15 |
16 | return { currentTime };
17 | }
18 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 |
6 | import "@fontsource/roboto/300.css";
7 | import "@fontsource/roboto/400.css";
8 | import "@fontsource/roboto/500.css";
9 | import "@fontsource/roboto/700.css";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 |
6 | import "@fontsource/roboto/300.css";
7 | import "@fontsource/roboto/400.css";
8 | import "@fontsource/roboto/500.css";
9 | import "@fontsource/roboto/700.css";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 |
6 | import "@fontsource/roboto/300.css";
7 | import "@fontsource/roboto/400.css";
8 | import "@fontsource/roboto/500.css";
9 | import "@fontsource/roboto/700.css";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 |
6 | import "@fontsource/roboto/300.css";
7 | import "@fontsource/roboto/400.css";
8 | import "@fontsource/roboto/500.css";
9 | import "@fontsource/roboto/700.css";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 |
6 | import "@fontsource/roboto/300.css";
7 | import "@fontsource/roboto/400.css";
8 | import "@fontsource/roboto/500.css";
9 | import "@fontsource/roboto/700.css";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 |
6 | import "@fontsource/roboto/300.css";
7 | import "@fontsource/roboto/400.css";
8 | import "@fontsource/roboto/500.css";
9 | import "@fontsource/roboto/700.css";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 |
6 | import "@fontsource/roboto/300.css";
7 | import "@fontsource/roboto/400.css";
8 | import "@fontsource/roboto/500.css";
9 | import "@fontsource/roboto/700.css";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 |
6 | import "@fontsource/roboto/300.css";
7 | import "@fontsource/roboto/400.css";
8 | import "@fontsource/roboto/500.css";
9 | import "@fontsource/roboto/700.css";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/features/forecast/AppNav.jsx:
--------------------------------------------------------------------------------
1 | import { Link, Breadcrumbs } from "@mui/material";
2 |
3 | function AppNav({ setIsHome }) {
4 | return (
5 |
6 | setIsHome(true)}>
7 | Home
8 |
9 |
10 | Forecast
11 |
12 |
13 | );
14 | }
15 | export default AppNav;
16 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | import Container from "./components/Container";
4 | import Forecast from "./components/Forecast";
5 | import Home from "./components/Home";
6 |
7 | function App() {
8 | const [isHome, setIsHome] = useState(true);
9 |
10 | return (
11 |
12 | {isHome ? (
13 |
14 | ) : (
15 |
16 | )}
17 |
18 | );
19 | }
20 |
21 | export default App;
22 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/src/useAdvice.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | export function useAdvice() {
4 | const [advice, setAdvice] = useState("");
5 | const [isLoading, setIsLoading] = useState(false);
6 |
7 | async function getAdvice() {
8 | setIsLoading(true);
9 |
10 | const response = await fetch("https://api.adviceslip.com/advice");
11 | const data = await response.json();
12 |
13 | setIsLoading(false);
14 | setAdvice(data.slip.advice);
15 | }
16 |
17 | useEffect(() => {
18 | getAdvice();
19 | }, []);
20 |
21 | return { isLoading, advice, getAdvice };
22 | }
23 |
--------------------------------------------------------------------------------
/part2/lecture-07/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | function App() {
4 | const [advice, setAdvice] = useState("");
5 | const [isLoading, setIsLoading] = useState(false);
6 |
7 | useEffect(() => {
8 | async function getAdvice() {
9 | const response = await fetch("https://api.adviceslip.com/advice");
10 | const data = await response.json();
11 |
12 | setAdvice(data.slip.advice);
13 | }
14 |
15 | getAdvice();
16 | }, [setAdvice]);
17 |
18 | return (
19 | <>
20 | Advice
21 | {advice}
22 | >
23 | );
24 | }
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/components/Day.jsx:
--------------------------------------------------------------------------------
1 | // Current weather
2 | // https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
3 |
4 | // Weather icon URL
5 | // https://openweathermap.org/img/wn/{weather icon}@2x.png
6 |
7 | function Day({ temperature, icon }) {
8 | const weatherIconUrl = `https://openweathermap.org/img/wn/${icon}@2x.png`;
9 |
10 | return (
11 |
12 |
13 | Today
14 |
15 | {Math.floor(temperature.min)}° — {Math.ceil(temperature.max)}
16 | °
17 |
18 |
19 | );
20 | }
21 | export default Day;
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/features/home/Day.jsx:
--------------------------------------------------------------------------------
1 | // Current weather
2 | // https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
3 |
4 | // Weather icon URL
5 | // https://openweathermap.org/img/wn/{weather icon}@2x.png
6 |
7 | function Day({ temperature, icon }) {
8 | const weatherIconUrl = `https://openweathermap.org/img/wn/${icon}@2x.png`;
9 |
10 | return (
11 |
12 |
13 | Today
14 |
15 | {Math.floor(temperature.min)}° — {Math.ceil(temperature.max)}
16 | °
17 |
18 |
19 | );
20 | }
21 | export default Day;
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | import { Button } from "@mui/material";
4 |
5 | import Day from "./Day";
6 | import styles from "./Home.module.css";
7 | import { useGeolocation } from "../hooks/useGeolocation";
8 |
9 | function Home() {
10 | const { position, getCurrentLocation } = useGeolocation();
11 |
12 | useEffect(() => {
13 | getCurrentLocation();
14 | }, []);
15 |
16 | return (
17 |
18 |
19 |
22 |
23 | );
24 | }
25 | export default Home;
26 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/features/forecast/Forecast.jsx:
--------------------------------------------------------------------------------
1 | import { useForecastWeather } from "../../hooks/useForecastWeather";
2 |
3 | import Loading from "../../ui/Loading";
4 | import AppNav from "./AppNav";
5 | import ForecastList from "./ForecastList";
6 |
7 | function Forecast({ setIsHome, position }) {
8 | const { weatherForecastList, isLoading } = useForecastWeather(position);
9 |
10 | return (
11 | <>
12 | {isLoading && }
13 | {!isLoading && (
14 | <>
15 |
16 |
17 |
18 | >
19 | )}
20 | >
21 | );
22 | }
23 | export default Forecast;
24 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/hooks/useGeolocation.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export function useGeolocation() {
4 | async function getCurrentLocation() {
5 | return new Promise((resolve, reject) => {
6 | if (!navigator.geolocation) {
7 | reject("Your browser does not support geolocation");
8 | }
9 |
10 | navigator.geolocation.getCurrentPosition(
11 | (position) => {
12 | const { latitude, longitude } = position.coords;
13 |
14 | resolve({ latitude, longitude });
15 | },
16 | (error) => {
17 | reject(error.message);
18 | }
19 | );
20 | });
21 | }
22 |
23 | return { getCurrentLocation };
24 | }
25 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/hooks/useGeolocation.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export function useGeolocation() {
4 | async function getCurrentLocation() {
5 | return new Promise((resolve, reject) => {
6 | if (!navigator.geolocation) {
7 | reject("Your browser does not support geolocation");
8 | }
9 |
10 | navigator.geolocation.getCurrentPosition(
11 | (position) => {
12 | const { latitude, longitude } = position.coords;
13 |
14 | resolve({ latitude, longitude });
15 | },
16 | (error) => {
17 | reject(error.message);
18 | }
19 | );
20 | });
21 | }
22 |
23 | return { getCurrentLocation };
24 | }
25 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/hooks/useGeolocation.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export function useGeolocation() {
4 | async function getCurrentLocation() {
5 | return new Promise((resolve, reject) => {
6 | if (!navigator.geolocation) {
7 | reject("Your browser does not support geolocation");
8 | }
9 |
10 | navigator.geolocation.getCurrentPosition(
11 | (position) => {
12 | const { latitude, longitude } = position.coords;
13 |
14 | resolve({ latitude, longitude });
15 | },
16 | (error) => {
17 | reject(error.message);
18 | }
19 | );
20 | });
21 | }
22 |
23 | return { getCurrentLocation };
24 | }
25 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/hooks/useGeolocation.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export function useGeolocation() {
4 | const [position, setPosition] = useState(null);
5 |
6 | function getCurrentLocation() {
7 | if (!navigator.geolocation) {
8 | alert("Your browser does not support geolocation");
9 | return;
10 | }
11 |
12 | navigator.geolocation.getCurrentPosition(
13 | (position) => {
14 | const { latitude, longitude } = position.coords;
15 |
16 | setPosition({ latitude, longitude });
17 | },
18 | (error) => {
19 | throw new Error(error.message);
20 | }
21 | );
22 | }
23 |
24 | return { getCurrentLocation, position };
25 | }
26 |
--------------------------------------------------------------------------------
/part1/challenge-01/final/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: 20px;
3 | font-family: sans-serif;
4 | }
5 | label {
6 | margin-bottom: 20px;
7 | font-size: 20px;
8 | display: block;
9 | }
10 | select {
11 | font-size: 20px;
12 | margin-bottom: 20px;
13 | }
14 | input[type="number"] {
15 | display: block;
16 | font-size: 20px;
17 | margin-bottom: 20px;
18 | }
19 | .circle {
20 | width: 150px;
21 | height: 150px;
22 | border-radius: 100%;
23 | background-color: #45d619;
24 | text-align: center;
25 | color: #fff;
26 | line-height: 150px;
27 | font-size: 32px;
28 | font-weight: bold;
29 | }
30 |
31 | .purple {
32 | background-color: purple;
33 | }
34 | .text-black {
35 | color: #424242;
36 | }
37 | .text-orange {
38 | color: #ffc26f;
39 | }
40 |
--------------------------------------------------------------------------------
/part1/challenge-01/start/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: 20px;
3 | font-family: sans-serif;
4 | }
5 | label {
6 | margin-bottom: 20px;
7 | font-size: 20px;
8 | display: block;
9 | }
10 | select {
11 | font-size: 20px;
12 | margin-bottom: 20px;
13 | }
14 | input[type="number"] {
15 | display: block;
16 | font-size: 20px;
17 | margin-bottom: 20px;
18 | }
19 | .circle {
20 | width: 150px;
21 | height: 150px;
22 | border-radius: 100%;
23 | background-color: #45d619;
24 | text-align: center;
25 | color: #fff;
26 | line-height: 150px;
27 | font-size: 32px;
28 | font-weight: bold;
29 | }
30 |
31 | .purple {
32 | background-color: purple;
33 | }
34 | .text-black {
35 | color: #424242;
36 | }
37 | .text-orange {
38 | color: #ffc26f;
39 | }
40 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: 20px;
3 | font-family: sans-serif;
4 | }
5 | label {
6 | margin-bottom: 20px;
7 | font-size: 20px;
8 | display: block;
9 | }
10 | select {
11 | font-size: 20px;
12 | margin-bottom: 20px;
13 | }
14 | input[type="number"] {
15 | display: block;
16 | font-size: 20px;
17 | margin-bottom: 20px;
18 | }
19 | .circle {
20 | width: 150px;
21 | height: 150px;
22 | border-radius: 100%;
23 | background-color: #45d619;
24 | text-align: center;
25 | color: #fff;
26 | line-height: 150px;
27 | font-size: 32px;
28 | font-weight: bold;
29 | }
30 |
31 | .purple {
32 | background-color: purple;
33 | }
34 | .text-black {
35 | color: #424242;
36 | }
37 | .text-orange {
38 | color: #ffc26f;
39 | }
40 |
--------------------------------------------------------------------------------
/part2/challenge-03/starter/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: 20px;
3 | font-family: sans-serif;
4 | }
5 | label {
6 | margin-bottom: 20px;
7 | font-size: 20px;
8 | display: block;
9 | }
10 | select {
11 | font-size: 20px;
12 | margin-bottom: 20px;
13 | }
14 | input[type="number"] {
15 | display: block;
16 | font-size: 20px;
17 | margin-bottom: 20px;
18 | }
19 | .circle {
20 | width: 150px;
21 | height: 150px;
22 | border-radius: 100%;
23 | background-color: #45d619;
24 | text-align: center;
25 | color: #fff;
26 | line-height: 150px;
27 | font-size: 32px;
28 | font-weight: bold;
29 | }
30 |
31 | .purple {
32 | background-color: purple;
33 | }
34 | .text-black {
35 | color: #424242;
36 | }
37 | .text-orange {
38 | color: #ffc26f;
39 | }
40 |
--------------------------------------------------------------------------------
/part2/lecture-08/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | function App() {
4 | const [advice, setAdvice] = useState("");
5 | const [isLoading, setIsLoading] = useState(false);
6 |
7 | async function getAdvice() {
8 | setIsLoading(true);
9 |
10 | const response = await fetch("https://api.adviceslip.com/advice");
11 | const data = await response.json();
12 |
13 | setIsLoading(false);
14 | setAdvice(data.slip.advice);
15 | }
16 |
17 | useEffect(() => {
18 | getAdvice();
19 | }, []);
20 |
21 | return (
22 | <>
23 | Advice
24 | {isLoading ? "Loading..." : advice}
25 |
26 | >
27 | );
28 | }
29 |
30 | export default App;
31 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import { Button, CircularProgress } from "@mui/material";
2 |
3 | import styles from "./Home.module.css";
4 |
5 | import { useCurrentWeather } from "../hooks/useCurrentWeather";
6 |
7 | import Day from "./Day";
8 |
9 | function Home() {
10 | const { temperature, weatherIcon, isLoading } = useCurrentWeather();
11 |
12 | return (
13 |
14 | {isLoading && }
15 | {!isLoading && (
16 | <>
17 |
18 |
21 | >
22 | )}
23 |
24 | );
25 | }
26 | export default Home;
27 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-03/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-04/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-04/starter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-05/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-06/starter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-07/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-08/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-09/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "final",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.66",
18 | "@types/react-dom": "^18.2.22",
19 | "@vitejs/plugin-react": "^4.2.1",
20 | "eslint": "^8.57.0",
21 | "eslint-plugin-react": "^7.34.1",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.6",
24 | "vite": "^5.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/part1/lecture-01/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
17 |
21 | Document
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/services/apiWeather.js:
--------------------------------------------------------------------------------
1 | const API_KEY = "8044c5ff43da29b1a91b760bf3a91592";
2 |
3 | export async function getCurrentWeather(lat, lon) {
4 | try {
5 | const response = await fetch(
6 | `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
7 | );
8 |
9 | return await response.json();
10 | } catch (error) {
11 | throw new Error(error.message);
12 | }
13 | }
14 |
15 | export async function getForecastWeather(lat, lon) {
16 | try {
17 | const response = await fetch(
18 | `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
19 | );
20 |
21 | return await response.json();
22 | } catch (error) {
23 | throw new Error(error.message);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/services/apiWeather.js:
--------------------------------------------------------------------------------
1 | const API_KEY = "8044c5ff43da29b1a91b760bf3a91592";
2 |
3 | export async function getCurrentWeather(lat, lon) {
4 | try {
5 | const response = await fetch(
6 | `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
7 | );
8 |
9 | return await response.json();
10 | } catch (error) {
11 | throw new Error(error.message);
12 | }
13 | }
14 |
15 | export async function getForecastWeather(lat, lon) {
16 | try {
17 | const response = await fetch(
18 | `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
19 | );
20 |
21 | return await response.json();
22 | } catch (error) {
23 | throw new Error(error.message);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/services/apiWeather.js:
--------------------------------------------------------------------------------
1 | const API_KEY = "8044c5ff43da29b1a91b760bf3a91592";
2 |
3 | export async function getCurrentWeather(lat, lon) {
4 | try {
5 | const response = await fetch(
6 | `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
7 | );
8 |
9 | return await response.json();
10 | } catch (error) {
11 | throw new Error(error.message);
12 | }
13 | }
14 |
15 | export async function getForecastWeather(lat, lon) {
16 | try {
17 | const response = await fetch(
18 | `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${API_KEY}`
19 | );
20 |
21 | return await response.json();
22 | } catch (error) {
23 | throw new Error(error.message);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/part2/lecture-07/final/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/part2/lecture-08/final/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/part2/lecture-09/final/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.4",
14 | "@emotion/styled": "^11.11.5",
15 | "@fontsource/roboto": "^5.0.13",
16 | "@mui/icons-material": "^5.15.17",
17 | "@mui/material": "^5.15.17",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.2.66",
23 | "@types/react-dom": "^18.2.22",
24 | "@vitejs/plugin-react": "^4.2.1",
25 | "vite": "^5.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/part1/challenge-02/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
14 |
18 |
19 |
20 | Document
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part1/challenge-02/starter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
14 |
18 |
19 |
20 | Document
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.4",
14 | "@emotion/styled": "^11.11.5",
15 | "@fontsource/roboto": "^5.0.13",
16 | "@mui/icons-material": "^5.15.17",
17 | "@mui/material": "^5.15.17",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.2.66",
23 | "@types/react-dom": "^18.2.22",
24 | "@vitejs/plugin-react": "^4.2.1",
25 | "vite": "^5.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.4",
14 | "@emotion/styled": "^11.11.5",
15 | "@fontsource/roboto": "^5.0.13",
16 | "@mui/icons-material": "^5.15.17",
17 | "@mui/material": "^5.15.17",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.2.66",
23 | "@types/react-dom": "^18.2.22",
24 | "@vitejs/plugin-react": "^4.2.1",
25 | "vite": "^5.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.4",
14 | "@emotion/styled": "^11.11.5",
15 | "@fontsource/roboto": "^5.0.13",
16 | "@mui/icons-material": "^5.15.17",
17 | "@mui/material": "^5.15.17",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.2.66",
23 | "@types/react-dom": "^18.2.22",
24 | "@vitejs/plugin-react": "^4.2.1",
25 | "vite": "^5.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.4",
14 | "@emotion/styled": "^11.11.5",
15 | "@fontsource/roboto": "^5.0.13",
16 | "@mui/icons-material": "^5.15.17",
17 | "@mui/material": "^5.15.17",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.2.66",
23 | "@types/react-dom": "^18.2.22",
24 | "@vitejs/plugin-react": "^4.2.1",
25 | "vite": "^5.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.4",
14 | "@emotion/styled": "^11.11.5",
15 | "@fontsource/roboto": "^5.0.13",
16 | "@mui/icons-material": "^5.15.17",
17 | "@mui/material": "^5.15.17",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.2.66",
23 | "@types/react-dom": "^18.2.22",
24 | "@vitejs/plugin-react": "^4.2.1",
25 | "vite": "^5.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.4",
14 | "@emotion/styled": "^11.11.5",
15 | "@fontsource/roboto": "^5.0.13",
16 | "@mui/icons-material": "^5.15.17",
17 | "@mui/material": "^5.15.17",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.2.66",
23 | "@types/react-dom": "^18.2.22",
24 | "@vitejs/plugin-react": "^4.2.1",
25 | "vite": "^5.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.4",
14 | "@emotion/styled": "^11.11.5",
15 | "@fontsource/roboto": "^5.0.13",
16 | "@mui/icons-material": "^5.15.17",
17 | "@mui/material": "^5.15.17",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.2.66",
23 | "@types/react-dom": "^18.2.22",
24 | "@vitejs/plugin-react": "^4.2.1",
25 | "vite": "^5.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/part1/lecture-04/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
14 |
18 |
19 |
20 | Document
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part1/lecture-04/starter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
14 |
18 |
19 |
20 | Document
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/components/Day.jsx:
--------------------------------------------------------------------------------
1 | // Current weather
2 | // https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
3 |
4 | // Weather icon URL
5 | // https://openweathermap.org/img/wn/{weather icon}@2x.png
6 |
7 | function Day({ temperature, icon }) {
8 | const weatherIconUrl = `https://openweathermap.org/img/wn/${icon}@2x.png`;
9 |
10 | function formatDay(dateStr) {
11 | return new Intl.DateTimeFormat("en", {
12 | weekday: "short",
13 | }).format(new Date(dateStr));
14 | }
15 |
16 | return (
17 |
18 |
19 | Today
20 |
21 | {Math.floor(temperature.min)}° — {Math.ceil(temperature.max)}
22 | °
23 |
24 |
25 | );
26 | }
27 | export default Day;
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/components/Day.jsx:
--------------------------------------------------------------------------------
1 | // Current weather
2 | // https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
3 |
4 | // Weather icon URL
5 | // https://openweathermap.org/img/wn/{weather icon}@2x.png
6 |
7 | function Day({ temperature, icon }) {
8 | const weatherIconUrl = `https://openweathermap.org/img/wn/${icon}@2x.png`;
9 |
10 | function formatDay(dateStr) {
11 | return new Intl.DateTimeFormat("en", {
12 | weekday: "short",
13 | }).format(new Date(dateStr));
14 | }
15 |
16 | return (
17 |
18 |
19 | Today
20 |
21 | {Math.floor(temperature.min)}° — {Math.ceil(temperature.max)}
22 | °
23 |
24 |
25 | );
26 | }
27 | export default Day;
28 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/components/Day.jsx:
--------------------------------------------------------------------------------
1 | // Current weather
2 | // https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
3 |
4 | // Weather icon URL
5 | // https://openweathermap.org/img/wn/{weather icon}@2x.png
6 |
7 | function Day({ temperature, icon }) {
8 | const weatherIconUrl = `https://openweathermap.org/img/wn/${icon}@2x.png`;
9 |
10 | function formatDay(dateStr) {
11 | return new Intl.DateTimeFormat("en", {
12 | weekday: "short",
13 | }).format(new Date(dateStr));
14 | }
15 |
16 | return (
17 |
18 |
19 | Today
20 |
21 | {Math.floor(temperature.min)}° — {Math.ceil(temperature.max)}
22 | °
23 |
24 |
25 | );
26 | }
27 | export default Day;
28 |
--------------------------------------------------------------------------------
/part1/lecture-03/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
17 |
21 |
22 |
23 | Document
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/part1/challenge-01/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
17 |
21 |
22 |
23 | Document
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/part1/challenge-01/start/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
17 |
21 |
22 |
23 | Document
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/part1/lecture-02/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
17 |
21 |
22 |
23 |
24 | Document
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/part2/lecture-03/starter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
17 |
21 |
22 |
23 | Document
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/part2/challenge-03/starter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
17 |
21 |
22 |
23 | Document
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import { Button, CircularProgress } from "@mui/material";
2 |
3 | import styles from "./Home.module.css";
4 |
5 | import { useCurrentWeather } from "../hooks/useCurrentWeather";
6 |
7 | import Day from "./Day";
8 |
9 | function Home({ setIsHome }) {
10 | const { temperature, weatherIcon, isLoading } = useCurrentWeather();
11 |
12 | return (
13 |
14 | {isLoading && }
15 | {!isLoading && (
16 | <>
17 |
18 |
25 | >
26 | )}
27 |
28 | );
29 | }
30 | export default Home;
31 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import { Button, CircularProgress } from "@mui/material";
2 |
3 | import styles from "./Home.module.css";
4 |
5 | import { useCurrentWeather } from "../hooks/useCurrentWeather";
6 |
7 | import Day from "./Day";
8 |
9 | function Home({ setIsHome, position }) {
10 | const { temperature, weatherIcon, isLoading } = useCurrentWeather(position);
11 |
12 | return (
13 |
14 | {isLoading && }
15 | {!isLoading && (
16 | <>
17 |
18 |
25 | >
26 | )}
27 |
28 | );
29 | }
30 | export default Home;
31 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import { Button, CircularProgress } from "@mui/material";
2 |
3 | import styles from "./Home.module.css";
4 |
5 | import { useCurrentWeather } from "../hooks/useCurrentWeather";
6 |
7 | import Day from "./Day";
8 |
9 | function Home({ setIsHome, position }) {
10 | const { temperature, weatherIcon, isLoading } = useCurrentWeather(position);
11 |
12 | return (
13 |
14 | {isLoading && }
15 | {!isLoading && (
16 | <>
17 |
18 |
25 | >
26 | )}
27 |
28 | );
29 | }
30 | export default Home;
31 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | import Container from "./components/Container";
4 | import Forecast from "./components/Forecast";
5 | import Home from "./components/Home";
6 | import { useGeolocation } from "./hooks/useGeolocation";
7 | import Loading from "./components/Loading";
8 |
9 | function App() {
10 | const { getCurrentLocation, isLoading, position } = useGeolocation();
11 |
12 | const [isHome, setIsHome] = useState(true);
13 |
14 | useEffect(() => {
15 | getCurrentLocation();
16 | }, []);
17 |
18 | return (
19 |
20 | {isLoading ? (
21 |
22 | ) : isHome ? (
23 |
24 | ) : (
25 |
26 | )}
27 |
28 | );
29 | }
30 |
31 | export default App;
32 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | import Container from "./components/Container";
4 | import Forecast from "./components/Forecast";
5 | import Home from "./components/Home";
6 | import { useGeolocation } from "./hooks/useGeolocation";
7 | import Loading from "./components/Loading";
8 |
9 | function App() {
10 | const { getCurrentLocation, isLoading, position } = useGeolocation();
11 |
12 | const [isHome, setIsHome] = useState(true);
13 |
14 | useEffect(() => {
15 | getCurrentLocation();
16 | }, []);
17 |
18 | return (
19 |
20 | {isLoading ? (
21 |
22 | ) : isHome ? (
23 |
24 | ) : (
25 |
26 | )}
27 |
28 | );
29 | }
30 |
31 | export default App;
32 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | import { useGeolocation } from "./hooks/useGeolocation";
4 |
5 | import Container from "./ui/Container";
6 | import Loading from "./ui/Loading";
7 |
8 | import Forecast from "./features/forecast/Forecast";
9 | import Home from "./features/home/Home";
10 |
11 | function App() {
12 | const { getCurrentLocation, isLoading, position } = useGeolocation();
13 |
14 | const [isHome, setIsHome] = useState(true);
15 |
16 | useEffect(() => {
17 | getCurrentLocation();
18 | }, []);
19 |
20 | return (
21 |
22 | {isLoading ? (
23 |
24 | ) : isHome ? (
25 |
26 | ) : (
27 |
28 | )}
29 |
30 | );
31 | }
32 |
33 | export default App;
34 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/features/home/Home.jsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@mui/material";
2 |
3 | import styles from "./Home.module.css";
4 |
5 | import { useCurrentWeather } from "../../hooks/useCurrentWeather";
6 |
7 | import Day from "./Day";
8 | import Loading from "../../ui/Loading";
9 |
10 | function Home({ setIsHome, position }) {
11 | const { temperature, weatherIcon, isLoading } = useCurrentWeather(position);
12 |
13 | return (
14 |
15 | {isLoading && }
16 | {!isLoading && (
17 | <>
18 |
19 |
26 | >
27 | )}
28 |
29 | );
30 | }
31 | export default Home;
32 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/components/Day.jsx:
--------------------------------------------------------------------------------
1 | // Current weather
2 | // https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
3 |
4 | // Weather icon URL
5 | // https://openweathermap.org/img/wn/{weather icon}@2x.png
6 |
7 | function Day({ date = "2024-04-03", max = 20.1, min = 10.2, code = "10n" }) {
8 | const isToday = date === new Date().toISOString().split("T")[0];
9 | const weatherIconUrl = `https://openweathermap.org/img/wn/${code}@2x.png`;
10 |
11 | function formatDay(dateStr) {
12 | return new Intl.DateTimeFormat("en", {
13 | weekday: "short",
14 | }).format(new Date(dateStr));
15 | }
16 |
17 | return (
18 |
19 |
20 | {isToday ? "Today" : formatDay(date)}
21 |
22 | {Math.floor(min)}° — {Math.ceil(max)}
23 | °
24 |
25 |
26 | );
27 | }
28 | export default Day;
29 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/components/Day.jsx:
--------------------------------------------------------------------------------
1 | // Current weather
2 | // https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
3 |
4 | // Weather icon URL
5 | // https://openweathermap.org/img/wn/{weather icon}@2x.png
6 |
7 | function Day({ date = "2024-04-03", max = 20.1, min = 10.2, code = "10n" }) {
8 | const isToday = date === new Date().toISOString().split("T")[0];
9 | const weatherIconUrl = `https://openweathermap.org/img/wn/${code}@2x.png`;
10 |
11 | function formatDay(dateStr) {
12 | return new Intl.DateTimeFormat("en", {
13 | weekday: "short",
14 | }).format(new Date(dateStr));
15 | }
16 |
17 | return (
18 |
19 |
20 | {isToday ? "Today" : formatDay(date)}
21 |
22 | {Math.floor(min)}° — {Math.ceil(max)}
23 | °
24 |
25 |
26 | );
27 | }
28 | export default Day;
29 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/components/Day.jsx:
--------------------------------------------------------------------------------
1 | // Current weather
2 | // https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
3 |
4 | // Weather icon URL
5 | // https://openweathermap.org/img/wn/{weather icon}@2x.png
6 |
7 | function Day({ date = "2024-04-03", max = 20.1, min = 10.2, code = "10n" }) {
8 | const isToday = date === new Date().toISOString().split("T")[0];
9 | const weatherIconUrl = `https://openweathermap.org/img/wn/${code}@2x.png`;
10 |
11 | function formatDay(dateStr) {
12 | return new Intl.DateTimeFormat("en", {
13 | weekday: "short",
14 | }).format(new Date(dateStr));
15 | }
16 |
17 | return (
18 |
19 |
20 | {isToday ? "Today" : formatDay(date)}
21 |
22 | {Math.floor(min)}° — {Math.ceil(max)}
23 | °
24 |
25 |
26 | );
27 | }
28 | export default Day;
29 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/hooks/useCurrentWeather.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 |
3 | import { getCurrentWeather } from "../services/apiWeather";
4 |
5 | export function useCurrentWeather(position) {
6 | const [temperature, setTemperature] = useState(null);
7 | const [isLoading, setIsLoading] = useState(true);
8 | const [weatherIcon, setWeatherIcon] = useState("");
9 |
10 | useEffect(() => {
11 | async function loadData() {
12 | setIsLoading(true);
13 |
14 | const weatherData = await getCurrentWeather(
15 | position.latitude,
16 | position.longitude
17 | );
18 |
19 | setTemperature({
20 | max: weatherData.main.temp_max,
21 | min: weatherData.main.temp_min,
22 | });
23 | setWeatherIcon(weatherData.weather[0].icon);
24 |
25 | setIsLoading(false);
26 | }
27 |
28 | loadData();
29 | }, []);
30 |
31 | return { temperature, isLoading, weatherIcon };
32 | }
33 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/hooks/useCurrentWeather.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 |
3 | import { getCurrentWeather } from "../services/apiWeather";
4 |
5 | export function useCurrentWeather(position) {
6 | const [temperature, setTemperature] = useState(null);
7 | const [isLoading, setIsLoading] = useState(true);
8 | const [weatherIcon, setWeatherIcon] = useState("");
9 |
10 | useEffect(() => {
11 | async function loadData() {
12 | setIsLoading(true);
13 |
14 | const weatherData = await getCurrentWeather(
15 | position.latitude,
16 | position.longitude
17 | );
18 |
19 | setTemperature({
20 | max: weatherData.main.temp_max,
21 | min: weatherData.main.temp_min,
22 | });
23 | setWeatherIcon(weatherData.weather[0].icon);
24 |
25 | setIsLoading(false);
26 | }
27 |
28 | loadData();
29 | }, []);
30 |
31 | return { temperature, isLoading, weatherIcon };
32 | }
33 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/hooks/useCurrentWeather.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 |
3 | import { getCurrentWeather } from "../services/apiWeather";
4 |
5 | export function useCurrentWeather(position) {
6 | const [temperature, setTemperature] = useState(null);
7 | const [isLoading, setIsLoading] = useState(true);
8 | const [weatherIcon, setWeatherIcon] = useState("");
9 |
10 | useEffect(() => {
11 | async function loadData() {
12 | setIsLoading(true);
13 |
14 | const weatherData = await getCurrentWeather(
15 | position.latitude,
16 | position.longitude
17 | );
18 |
19 | setTemperature({
20 | max: weatherData.main.temp_max,
21 | min: weatherData.main.temp_min,
22 | });
23 | setWeatherIcon(weatherData.weather[0].icon);
24 |
25 | setIsLoading(false);
26 | }
27 |
28 | loadData();
29 | }, []);
30 |
31 | return { temperature, isLoading, weatherIcon };
32 | }
33 |
--------------------------------------------------------------------------------
/part1/challenge-02/starter/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | 1. Implement adding and deleting todo items.
3 | 2. Implement completing todo items (completed items should be moved to the bottom).
4 | */
5 |
6 | function MyApp() {
7 | return (
8 |
9 | React Todo List
10 |
11 |
12 |
27 |
28 | );
29 | }
30 |
31 | const appEl = document.querySelector("#app");
32 | const root = ReactDOM.createRoot(appEl);
33 |
34 | root.render();
35 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | import { Button } from "@mui/material";
4 |
5 | import Day from "./Day";
6 | import styles from "./Home.module.css";
7 | import { useGeolocation } from "../hooks/useGeolocation";
8 | import { getCurrentWeather } from "../services/apiWeather";
9 |
10 | function Home() {
11 | const { getCurrentLocation } = useGeolocation();
12 |
13 | useEffect(() => {
14 | async function loadData() {
15 | const position = await getCurrentLocation();
16 |
17 | const weatherData = await getCurrentWeather(
18 | position.latitude,
19 | position.longitude
20 | );
21 | console.log("weatherData: ", weatherData);
22 | }
23 |
24 | loadData();
25 | }, []);
26 |
27 | return (
28 |
29 |
30 |
33 |
34 | );
35 | }
36 | export default Home;
37 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/hooks/useGeolocation.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export function useGeolocation() {
4 | const [isLoading, setIsLoading] = useState(true);
5 | const [position, setPosition] = useState(null);
6 |
7 | async function getCurrentLocation() {
8 | return new Promise((resolve, reject) => {
9 | setIsLoading(true);
10 |
11 | if (!navigator.geolocation) {
12 | setIsLoading(false);
13 | reject("Your browser does not support geolocation");
14 | }
15 |
16 | navigator.geolocation.getCurrentPosition(
17 | (position) => {
18 | const { latitude, longitude } = position.coords;
19 |
20 | setIsLoading(false);
21 | setPosition({ latitude, longitude });
22 | resolve();
23 | },
24 | (error) => {
25 | setIsLoading(false);
26 | reject(error.message);
27 | }
28 | );
29 | });
30 | }
31 |
32 | return { getCurrentLocation, position, isLoading };
33 | }
34 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/hooks/useGeolocation.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export function useGeolocation() {
4 | const [isLoading, setIsLoading] = useState(true);
5 | const [position, setPosition] = useState(null);
6 |
7 | async function getCurrentLocation() {
8 | return new Promise((resolve, reject) => {
9 | setIsLoading(true);
10 |
11 | if (!navigator.geolocation) {
12 | setIsLoading(false);
13 | reject("Your browser does not support geolocation");
14 | }
15 |
16 | navigator.geolocation.getCurrentPosition(
17 | (position) => {
18 | const { latitude, longitude } = position.coords;
19 |
20 | setIsLoading(false);
21 | setPosition({ latitude, longitude });
22 | resolve();
23 | },
24 | (error) => {
25 | setIsLoading(false);
26 | reject(error.message);
27 | }
28 | );
29 | });
30 | }
31 |
32 | return { getCurrentLocation, position, isLoading };
33 | }
34 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/hooks/useGeolocation.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export function useGeolocation() {
4 | const [isLoading, setIsLoading] = useState(true);
5 | const [position, setPosition] = useState(null);
6 |
7 | async function getCurrentLocation() {
8 | return new Promise((resolve, reject) => {
9 | setIsLoading(true);
10 |
11 | if (!navigator.geolocation) {
12 | setIsLoading(false);
13 | reject("Your browser does not support geolocation");
14 | }
15 |
16 | navigator.geolocation.getCurrentPosition(
17 | (position) => {
18 | const { latitude, longitude } = position.coords;
19 |
20 | setIsLoading(false);
21 | setPosition({ latitude, longitude });
22 | resolve();
23 | },
24 | (error) => {
25 | setIsLoading(false);
26 | reject(error.message);
27 | }
28 | );
29 | });
30 | }
31 |
32 | return { getCurrentLocation, position, isLoading };
33 | }
34 |
--------------------------------------------------------------------------------
/part1/challenge-01/start/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | height: 0px,
3 | width: 0px,
4 | lineHeight: 0px,
5 | transform: `rotate(0deg)`,
6 | */
7 |
8 | function MyApp() {
9 | return (
10 |
11 |
15 |
16 |
26 |
27 |
31 |
32 |
36 | Hi!
37 |
38 | );
39 | }
40 |
41 | const appEl = document.querySelector("#app");
42 | const root = ReactDOM.createRoot(appEl);
43 |
44 | root.render();
45 |
--------------------------------------------------------------------------------
/part2/lecture-09/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | function App() {
4 | const [advice, setAdvice] = useState("");
5 | const [isLoading, setIsLoading] = useState(false);
6 |
7 | const [currentTime, setCurrentTime] = useState(new Date().toLocaleString());
8 |
9 | async function getAdvice() {
10 | setIsLoading(true);
11 |
12 | const response = await fetch("https://api.adviceslip.com/advice");
13 | const data = await response.json();
14 |
15 | setIsLoading(false);
16 | setAdvice(data.slip.advice);
17 | }
18 |
19 | useEffect(() => {
20 | getAdvice();
21 |
22 | const interval = setInterval(() => {
23 | setCurrentTime(new Date().toLocaleString());
24 | }, 1000);
25 |
26 | return () => {
27 | clearInterval(interval);
28 | };
29 | }, []);
30 |
31 | return (
32 | <>
33 | Advice
34 | {currentTime}
35 | {isLoading ? "Loading..." : advice}
36 |
37 | >
38 | );
39 | }
40 |
41 | export default App;
42 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/hooks/useCurrentWeather.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { useGeolocation } from "./useGeolocation";
3 |
4 | import { getCurrentWeather } from "../services/apiWeather";
5 |
6 | export function useCurrentWeather() {
7 | const { getCurrentLocation } = useGeolocation();
8 |
9 | const [temperature, setTemperature] = useState(null);
10 | const [isLoading, setIsLoading] = useState(true);
11 | const [weatherIcon, setWeatherIcon] = useState("");
12 |
13 | useEffect(() => {
14 | async function loadData() {
15 | setIsLoading(true);
16 |
17 | const position = await getCurrentLocation();
18 |
19 | const weatherData = await getCurrentWeather(
20 | position.latitude,
21 | position.longitude
22 | );
23 |
24 | setTemperature({
25 | max: weatherData.main.temp_max,
26 | min: weatherData.main.temp_min,
27 | });
28 | setWeatherIcon(weatherData.weather[0].icon);
29 |
30 | setIsLoading(false);
31 | }
32 |
33 | loadData();
34 | }, []);
35 |
36 | return { temperature, isLoading, weatherIcon };
37 | }
38 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/hooks/useCurrentWeather.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { useGeolocation } from "./useGeolocation";
3 |
4 | import { getCurrentWeather } from "../services/apiWeather";
5 |
6 | export function useCurrentWeather() {
7 | const { getCurrentLocation } = useGeolocation();
8 |
9 | const [temperature, setTemperature] = useState(null);
10 | const [isLoading, setIsLoading] = useState(true);
11 | const [weatherIcon, setWeatherIcon] = useState("");
12 |
13 | useEffect(() => {
14 | async function loadData() {
15 | setIsLoading(true);
16 |
17 | const position = await getCurrentLocation();
18 |
19 | const weatherData = await getCurrentWeather(
20 | position.latitude,
21 | position.longitude
22 | );
23 |
24 | setTemperature({
25 | max: weatherData.main.temp_max,
26 | min: weatherData.main.temp_min,
27 | });
28 | setWeatherIcon(weatherData.weather[0].icon);
29 |
30 | setIsLoading(false);
31 | }
32 |
33 | loadData();
34 | }, []);
35 |
36 | return { temperature, isLoading, weatherIcon };
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Uaena_Alex_John
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/features/forecast/ForecastList.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | List,
3 | ListItem,
4 | ListItemAvatar,
5 | ListItemText,
6 | Avatar,
7 | } from "@mui/material";
8 |
9 | function ForecastList({ weatherForecastList }) {
10 | function formatDay(dateStr) {
11 | return new Intl.DateTimeFormat("en", {
12 | weekday: "short",
13 | }).format(new Date(dateStr));
14 | }
15 |
16 | return (
17 |
18 | {weatherForecastList.map((weatherForecast, idx) => (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
29 |
30 | {Math.floor(weatherForecast.min)}°/
31 | {Math.ceil(weatherForecast.max)}
32 | °
33 |
34 |
35 | ))}
36 |
37 | );
38 | }
39 |
40 | export default ForecastList;
41 |
--------------------------------------------------------------------------------
/part2/lecture-05/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import "./App.css";
3 |
4 | import AppInput from "./AppInput";
5 |
6 | function App() {
7 | const [username, setUsername] = useState("");
8 | const [password, setPassword] = useState("");
9 |
10 | const usernameClass = username.length <= 5 ? "input-error" : "input";
11 | const passwordClass = password.length <= 5 ? "input-error" : "input";
12 |
13 | function handleSubmit(event) {
14 | event.preventDefault();
15 |
16 | if (usernameClass === "input-error" || passwordClass === "input-error") {
17 | return;
18 | }
19 |
20 | console.log("username: ", username);
21 | console.log("password: ", password);
22 |
23 | setUsername("");
24 | setPassword("");
25 | }
26 |
27 | return (
28 |
29 | Login Form
30 |
50 |
51 | );
52 | }
53 |
54 | export default App;
55 |
--------------------------------------------------------------------------------
/part2/lecture-06/starter/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import "./App.css";
3 |
4 | import AppInput from "./AppInput";
5 |
6 | function App() {
7 | const [username, setUsername] = useState("");
8 | const [password, setPassword] = useState("");
9 |
10 | const usernameClass = username.length <= 5 ? "input-error" : "input";
11 | const passwordClass = password.length <= 5 ? "input-error" : "input";
12 |
13 | function handleSubmit(event) {
14 | event.preventDefault();
15 |
16 | if (usernameClass === "input-error" || passwordClass === "input-error") {
17 | return;
18 | }
19 |
20 | console.log("username: ", username);
21 | console.log("password: ", password);
22 |
23 | setUsername("");
24 | setPassword("");
25 | }
26 |
27 | return (
28 |
29 | Login Form
30 |
50 |
51 | );
52 | }
53 |
54 | export default App;
55 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-03/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-03/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import "./App.css";
3 |
4 | function App() {
5 | const [username, setUsername] = useState("");
6 | const [password, setPassword] = useState("");
7 |
8 | const usernameClass = username.length <= 5 ? "input-error" : "input";
9 | const passwordClass = password.length <= 5 ? "input-error" : "input";
10 |
11 | function handleSubmit(event) {
12 | event.preventDefault();
13 |
14 | if (usernameClass === "input-error" || passwordClass === "input-error") {
15 | return;
16 | }
17 |
18 | console.log("username: ", username);
19 | console.log("password: ", password);
20 |
21 | setUsername("");
22 | setPassword("");
23 | }
24 |
25 | return (
26 |
27 | Login Form
28 |
48 |
49 | );
50 | }
51 |
52 | export default App;
53 |
--------------------------------------------------------------------------------
/part2/lecture-04/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-04/starter/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-05/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-06/starter/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-07/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-08/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-09/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-10/final/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-04/starter/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import "./App.css";
3 |
4 | function App() {
5 | const [username, setUsername] = useState("");
6 | const [password, setPassword] = useState("");
7 |
8 | const usernameClass = username.length <= 5 ? "input-error" : "input";
9 | const passwordClass = password.length <= 5 ? "input-error" : "input";
10 |
11 | function handleSubmit(event) {
12 | event.preventDefault();
13 |
14 | if (usernameClass === "input-error" || passwordClass === "input-error") {
15 | return;
16 | }
17 |
18 | console.log("username: ", username);
19 | console.log("password: ", password);
20 |
21 | setUsername("");
22 | setPassword("");
23 | }
24 |
25 | return (
26 |
27 | Login Form
28 |
48 |
49 | );
50 | }
51 |
52 | export default App;
53 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part1/lecture-02/main.js:
--------------------------------------------------------------------------------
1 | function AppContent() {
2 | const [username, setUsername] = React.useState("");
3 | const [password, setPassword] = React.useState("");
4 |
5 | function handleSubmit(event) {
6 | event.preventDefault();
7 |
8 | if (username === "" || password === "") {
9 | alert('"username" and "password" are required');
10 | return;
11 | }
12 |
13 | console.log("username: ", username);
14 | console.log("password: ", password);
15 |
16 | setUsername("");
17 | setPassword("");
18 | }
19 |
20 | // JSX syntax
21 | return (
22 |
23 | Login Form
24 |
51 |
52 | );
53 | }
54 |
55 | const appEl = document.querySelector("#app");
56 | const root = ReactDOM.createRoot(appEl);
57 |
58 | root.render();
59 |
--------------------------------------------------------------------------------
/part2/lecture-04/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import UsernameInput from "./UsernameInput";
3 | import "./App.css";
4 |
5 | function App() {
6 | const [username, setUsername] = useState("");
7 | const [password, setPassword] = useState("");
8 |
9 | const usernameClass = username.length <= 5 ? "input-error" : "input";
10 | const passwordClass = password.length <= 5 ? "input-error" : "input";
11 |
12 | function handleSubmit(event) {
13 | event.preventDefault();
14 |
15 | if (usernameClass === "input-error" || passwordClass === "input-error") {
16 | return;
17 | }
18 |
19 | console.log("username: ", username);
20 | console.log("password: ", password);
21 |
22 | setUsername("");
23 | setPassword("");
24 | }
25 |
26 | return (
27 |
28 | Login Form
29 |
48 |
49 | );
50 | }
51 |
52 | export default App;
53 |
--------------------------------------------------------------------------------
/part2/lecture-05/starter/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import UsernameInput from "./UsernameInput";
3 | import "./App.css";
4 | import PasswordInput from "./PasswordInput";
5 |
6 | function App() {
7 | const [username, setUsername] = useState("");
8 | const [password, setPassword] = useState("");
9 |
10 | const usernameClass = username.length <= 5 ? "input-error" : "input";
11 | const passwordClass = password.length <= 5 ? "input-error" : "input";
12 |
13 | function handleSubmit(event) {
14 | event.preventDefault();
15 |
16 | if (usernameClass === "input-error" || passwordClass === "input-error") {
17 | return;
18 | }
19 |
20 | console.log("username: ", username);
21 | console.log("password: ", password);
22 |
23 | setUsername("");
24 | setPassword("");
25 | }
26 |
27 | return (
28 |
29 | Login Form
30 |
49 |
50 | );
51 | }
52 |
53 | export default App;
54 |
--------------------------------------------------------------------------------
/part2/lecture-06/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import "./App.css";
3 |
4 | import AppInput from "./AppInput";
5 | import AppButton from "./AppButton";
6 |
7 | function App() {
8 | const [username, setUsername] = useState("");
9 | const [password, setPassword] = useState("");
10 |
11 | const usernameClass = username.length <= 5 ? "input-error" : "input";
12 | const passwordClass = password.length <= 5 ? "input-error" : "input";
13 |
14 | function handleSubmit(event) {
15 | event.preventDefault();
16 |
17 | if (usernameClass === "input-error" || passwordClass === "input-error") {
18 | return;
19 | }
20 |
21 | console.log("username: ", username);
22 | console.log("password: ", password);
23 |
24 | setUsername("");
25 | setPassword("");
26 | }
27 |
28 | return (
29 |
30 | Login Form
31 |
52 |
53 | );
54 | }
55 |
56 | export default App;
57 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import AppInput from "./AppInput";
3 | import "./App.css";
4 |
5 | function App() {
6 | const [isPurple, setIsPurple] = useState("");
7 | const [textColor, setTextColor] = useState("");
8 |
9 | const [size, setSize] = useState(150);
10 | const [rotate, setRotate] = useState(0);
11 |
12 | const circleStyle = {
13 | height: `${size}px`,
14 | width: `${size}px`,
15 | lineHeight: `${size}px`,
16 |
17 | transform: `rotate(${rotate}deg)`,
18 | };
19 |
20 | return (
21 |
22 |
30 |
31 |
42 |
43 |
44 | Circle Size
45 |
46 |
47 |
48 | Circle Rotate
49 |
50 |
51 |
55 | Hi!
56 |
57 |
58 | );
59 | }
60 |
61 | export default App;
62 |
--------------------------------------------------------------------------------
/part2/weather-app/final-07/src/hooks/useForecastWeather.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useState } from "react";
3 | import { getForecastWeather } from "../services/apiWeather";
4 |
5 | export function useForecastWeather(position) {
6 | const [weatherForecastList, setWeatherForecastList] = useState([]);
7 | const [isLoading, setIsLoading] = useState(true);
8 |
9 | async function loadForecastData() {
10 | setIsLoading(true);
11 |
12 | const weatherData = await getForecastWeather(
13 | position.latitude,
14 | position.longitude
15 | );
16 |
17 | const weatherDataList = weatherData.list;
18 |
19 | const filteredForecastList = weatherDataList
20 | .map((weatherData) => {
21 | const weather = weatherData.weather[0];
22 |
23 | return {
24 | weatherIcon: `https://openweathermap.org/img/wn/${weather.icon}@2x.png`,
25 | weather: weather.main,
26 | min: weatherData.main.temp_max,
27 | max: weatherData.main.temp_min,
28 | date: weatherData.dt_txt,
29 | };
30 | })
31 | .filter((weatherData) => weatherData.date.includes("12:00:00"))
32 | .filter((weatherData) => {
33 | const curDate = new Date().getDate();
34 | const weatherDate = new Date(weatherData.date).getDate();
35 |
36 | return curDate !== weatherDate;
37 | });
38 |
39 | setWeatherForecastList(filteredForecastList);
40 |
41 | setIsLoading(false);
42 | }
43 |
44 | useEffect(() => {
45 | loadForecastData();
46 | }, []);
47 |
48 | return { isLoading, weatherForecastList };
49 | }
50 |
--------------------------------------------------------------------------------
/part1/lecture-03/main.js:
--------------------------------------------------------------------------------
1 | function AppContent() {
2 | const [username, setUsername] = React.useState("");
3 | const [password, setPassword] = React.useState("");
4 |
5 | const usernameClass =
6 | username.length <= 5 ? "input-error" : "input";
7 | const passwordClass =
8 | password.length <= 5 ? "input-error" : "input";
9 |
10 | function handleSubmit(event) {
11 | event.preventDefault();
12 |
13 | if (
14 | usernameClass === "input-error" ||
15 | passwordClass === "input-error"
16 | ) {
17 | return;
18 | }
19 |
20 | console.log("username: ", username);
21 | console.log("password: ", password);
22 |
23 | setUsername("");
24 | setPassword("");
25 | }
26 |
27 | // JSX syntax
28 | return (
29 |
30 | Login Form
31 |
58 |
59 | );
60 | }
61 |
62 | const appEl = document.querySelector("#app");
63 | const root = ReactDOM.createRoot(appEl);
64 |
65 | root.render();
66 |
--------------------------------------------------------------------------------
/part2/lecture-03/starter/main.js:
--------------------------------------------------------------------------------
1 | function AppContent() {
2 | const [username, setUsername] = React.useState("");
3 | const [password, setPassword] = React.useState("");
4 |
5 | const usernameClass =
6 | username.length <= 5 ? "input-error" : "input";
7 | const passwordClass =
8 | password.length <= 5 ? "input-error" : "input";
9 |
10 | function handleSubmit(event) {
11 | event.preventDefault();
12 |
13 | if (
14 | usernameClass === "input-error" ||
15 | passwordClass === "input-error"
16 | ) {
17 | return;
18 | }
19 |
20 | console.log("username: ", username);
21 | console.log("password: ", password);
22 |
23 | setUsername("");
24 | setPassword("");
25 | }
26 |
27 | // JSX syntax
28 | return (
29 |
30 | Login Form
31 |
58 |
59 | );
60 | }
61 |
62 | const appEl = document.querySelector("#app");
63 | const root = ReactDOM.createRoot(appEl);
64 |
65 | root.render();
66 |
--------------------------------------------------------------------------------
/part2/weather-app/final-01/src/components/Forecast.jsx:
--------------------------------------------------------------------------------
1 | import { List, ListItem, ListItemAvatar, ListItemText } from "@mui/material";
2 | import Avatar from "@mui/material/Avatar";
3 |
4 | // Weather forecast
5 | // https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}
6 |
7 | function Forecast() {
8 | const weatherForecasts = [
9 | {
10 | id: 1,
11 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
12 | min: "20",
13 | max: "30",
14 | weather: "Rain",
15 | date: "2024-04-03",
16 | },
17 | {
18 | id: 2,
19 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
20 | min: "20",
21 | max: "30",
22 | weather: "Rain",
23 | date: "2024-04-03",
24 | },
25 | {
26 | id: 3,
27 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
28 | min: "20",
29 | max: "30",
30 | weather: "Rain",
31 | date: "2024-04-03",
32 | },
33 | {
34 | id: 4,
35 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
36 | min: "20",
37 | max: "30",
38 | weather: "Rain",
39 | date: "2024-04-03",
40 | },
41 | ];
42 |
43 | return (
44 |
45 | {weatherForecasts.map((weatherForecast) => (
46 |
47 |
48 |
49 |
50 |
51 |
52 |
56 |
57 | {Math.floor(weatherForecast.min)}°/
58 | {Math.ceil(weatherForecast.max)}
59 | °
60 |
61 |
62 | ))}
63 |
64 | );
65 | }
66 | export default Forecast;
67 |
--------------------------------------------------------------------------------
/part2/weather-app/final-02/src/components/Forecast.jsx:
--------------------------------------------------------------------------------
1 | import { List, ListItem, ListItemAvatar, ListItemText } from "@mui/material";
2 | import Avatar from "@mui/material/Avatar";
3 |
4 | // Weather forecast
5 | // https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}
6 |
7 | function Forecast() {
8 | const weatherForecasts = [
9 | {
10 | id: 1,
11 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
12 | min: "20",
13 | max: "30",
14 | weather: "Rain",
15 | date: "2024-04-03",
16 | },
17 | {
18 | id: 2,
19 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
20 | min: "20",
21 | max: "30",
22 | weather: "Rain",
23 | date: "2024-04-03",
24 | },
25 | {
26 | id: 3,
27 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
28 | min: "20",
29 | max: "30",
30 | weather: "Rain",
31 | date: "2024-04-03",
32 | },
33 | {
34 | id: 4,
35 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
36 | min: "20",
37 | max: "30",
38 | weather: "Rain",
39 | date: "2024-04-03",
40 | },
41 | ];
42 |
43 | return (
44 |
45 | {weatherForecasts.map((weatherForecast) => (
46 |
47 |
48 |
49 |
50 |
51 |
52 |
56 |
57 | {Math.floor(weatherForecast.min)}°/
58 | {Math.ceil(weatherForecast.max)}
59 | °
60 |
61 |
62 | ))}
63 |
64 | );
65 | }
66 | export default Forecast;
67 |
--------------------------------------------------------------------------------
/part2/weather-app/final-03/src/components/Forecast.jsx:
--------------------------------------------------------------------------------
1 | import { List, ListItem, ListItemAvatar, ListItemText } from "@mui/material";
2 | import Avatar from "@mui/material/Avatar";
3 |
4 | // Weather forecast
5 | // https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}
6 |
7 | function Forecast() {
8 | const weatherForecasts = [
9 | {
10 | id: 1,
11 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
12 | min: "20",
13 | max: "30",
14 | weather: "Rain",
15 | date: "2024-04-03",
16 | },
17 | {
18 | id: 2,
19 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
20 | min: "20",
21 | max: "30",
22 | weather: "Rain",
23 | date: "2024-04-03",
24 | },
25 | {
26 | id: 3,
27 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
28 | min: "20",
29 | max: "30",
30 | weather: "Rain",
31 | date: "2024-04-03",
32 | },
33 | {
34 | id: 4,
35 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
36 | min: "20",
37 | max: "30",
38 | weather: "Rain",
39 | date: "2024-04-03",
40 | },
41 | ];
42 |
43 | return (
44 |
45 | {weatherForecasts.map((weatherForecast) => (
46 |
47 |
48 |
49 |
50 |
51 |
52 |
56 |
57 | {Math.floor(weatherForecast.min)}°/
58 | {Math.ceil(weatherForecast.max)}
59 | °
60 |
61 |
62 | ))}
63 |
64 | );
65 | }
66 | export default Forecast;
67 |
--------------------------------------------------------------------------------
/part2/weather-app/starter/src/components/Forecast.jsx:
--------------------------------------------------------------------------------
1 | import { List, ListItem, ListItemAvatar, ListItemText } from "@mui/material";
2 | import Avatar from "@mui/material/Avatar";
3 |
4 | // Weather forecast
5 | // https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}
6 |
7 | function Forecast() {
8 | const weatherForecasts = [
9 | {
10 | id: 1,
11 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
12 | min: "20",
13 | max: "30",
14 | weather: "Rain",
15 | date: "2024-04-03",
16 | },
17 | {
18 | id: 2,
19 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
20 | min: "20",
21 | max: "30",
22 | weather: "Rain",
23 | date: "2024-04-03",
24 | },
25 | {
26 | id: 3,
27 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
28 | min: "20",
29 | max: "30",
30 | weather: "Rain",
31 | date: "2024-04-03",
32 | },
33 | {
34 | id: 4,
35 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
36 | min: "20",
37 | max: "30",
38 | weather: "Rain",
39 | date: "2024-04-03",
40 | },
41 | ];
42 |
43 | return (
44 |
45 | {weatherForecasts.map((weatherForecast) => (
46 |
47 |
48 |
49 |
50 |
51 |
52 |
56 |
57 | {Math.floor(weatherForecast.min)}°/
58 | {Math.ceil(weatherForecast.max)}
59 | °
60 |
61 |
62 | ))}
63 |
64 | );
65 | }
66 | export default Forecast;
67 |
--------------------------------------------------------------------------------
/part1/challenge-01/final/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | height: 0px,
3 | width: 0px,
4 | lineHeight: 0px,
5 |
6 | transform: `rotate(0deg)`,
7 | */
8 |
9 | function MyApp() {
10 | const [isPurple, setIsPurple] = React.useState("");
11 | const [textColor, setTextColor] = React.useState("");
12 |
13 | const [size, setSize] = React.useState(150);
14 | const [rotate, setRotate] = React.useState(0);
15 |
16 | const circleStyle = {
17 | height: `${size}px`,
18 | width: `${size}px`,
19 | lineHeight: `${size}px`,
20 |
21 | transform: `rotate(${rotate}deg)`,
22 | };
23 |
24 | return (
25 |
26 |
34 |
35 |
50 |
51 |
59 |
60 |
70 |
76 | Hi!
77 |
78 |
79 | );
80 | }
81 |
82 | const appEl = document.querySelector("#app");
83 | const root = ReactDOM.createRoot(appEl);
84 |
85 | root.render();
86 |
--------------------------------------------------------------------------------
/part2/challenge-03/starter/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | height: 0px,
3 | width: 0px,
4 | lineHeight: 0px,
5 |
6 | transform: `rotate(0deg)`,
7 | */
8 |
9 | function MyApp() {
10 | const [isPurple, setIsPurple] = React.useState("");
11 | const [textColor, setTextColor] = React.useState("");
12 |
13 | const [size, setSize] = React.useState(150);
14 | const [rotate, setRotate] = React.useState(0);
15 |
16 | const circleStyle = {
17 | height: `${size}px`,
18 | width: `${size}px`,
19 | lineHeight: `${size}px`,
20 |
21 | transform: `rotate(${rotate}deg)`,
22 | };
23 |
24 | return (
25 |
26 |
34 |
35 |
50 |
51 |
59 |
60 |
70 |
76 | Hi!
77 |
78 |
79 | );
80 | }
81 |
82 | const appEl = document.querySelector("#app");
83 | const root = ReactDOM.createRoot(appEl);
84 |
85 | root.render();
86 |
--------------------------------------------------------------------------------
/part2/weather-app/final-04/src/components/Forecast.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | List,
3 | ListItem,
4 | ListItemAvatar,
5 | ListItemText,
6 | Link,
7 | Breadcrumbs,
8 | Typography,
9 | } from "@mui/material";
10 | import Avatar from "@mui/material/Avatar";
11 |
12 | // Weather forecast
13 | // https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}
14 |
15 | function Forecast({ setIsHome }) {
16 | const weatherForecasts = [
17 | {
18 | id: 1,
19 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
20 | min: "20",
21 | max: "30",
22 | weather: "Rain",
23 | date: "2024-04-03",
24 | },
25 | {
26 | id: 2,
27 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
28 | min: "20",
29 | max: "30",
30 | weather: "Rain",
31 | date: "2024-04-03",
32 | },
33 | {
34 | id: 3,
35 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
36 | min: "20",
37 | max: "30",
38 | weather: "Rain",
39 | date: "2024-04-03",
40 | },
41 | {
42 | id: 4,
43 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
44 | min: "20",
45 | max: "30",
46 | weather: "Rain",
47 | date: "2024-04-03",
48 | },
49 | ];
50 |
51 | return (
52 | <>
53 |
54 | setIsHome(true)}>
55 | Home
56 |
57 |
58 | Forecast
59 |
60 |
61 |
62 |
63 | {weatherForecasts.map((weatherForecast) => (
64 |
65 |
66 |
67 |
68 |
69 |
70 |
74 |
75 | {Math.floor(weatherForecast.min)}°/
76 | {Math.ceil(weatherForecast.max)}
77 | °
78 |
79 |
80 | ))}
81 |
82 | >
83 | );
84 | }
85 | export default Forecast;
86 |
--------------------------------------------------------------------------------
/part1/challenge-02/final/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | 1. Implement adding and deleting todo items.
3 | 2. Implement completing todo items (completed items should be moved to the bottom).
4 | */
5 |
6 | function MyApp() {
7 | const [currentTodoItem, setCurrentTodoItem] = React.useState("");
8 | const [todoList, setTodoList] = React.useState([
9 | {
10 | id: 1,
11 | item: "Learn React",
12 | isCompleted: false,
13 | },
14 | {
15 | id: 2,
16 | item: "Learn Svelte",
17 | isCompleted: true,
18 | },
19 | ]);
20 |
21 | const sortTodo = (o1, o2) => {
22 | if (o1.isCompleted !== o2.isCompleted) {
23 | return o1.isCompleted ? 1 : -1;
24 | }
25 |
26 | return o1.id - o2.id;
27 | };
28 |
29 | function handleAddTodo() {
30 | if (!currentTodoItem.length) {
31 | alert("Please enter a todo item");
32 | return;
33 | }
34 |
35 | const addedList = [
36 | ...todoList,
37 | {
38 | id: Math.random() + Date.now(),
39 | item: currentTodoItem,
40 | isCompleted: false,
41 | },
42 | ];
43 | addedList.sort(sortTodo);
44 |
45 | setTodoList(addedList);
46 |
47 | setCurrentTodoItem("");
48 | }
49 |
50 | function handleDeleteTodo(id) {
51 | const deletedTodoList = todoList.filter((todo) => todo.id !== id);
52 | deletedTodoList.sort(sortTodo);
53 |
54 | setTodoList(deletedTodoList);
55 | }
56 |
57 | function handleToggleTodo(id) {
58 | const toggledTodoList = todoList.map((todo) => {
59 | if (todo.id === id) {
60 | return {
61 | ...todo,
62 | isCompleted: !todo.isCompleted,
63 | };
64 | }
65 |
66 | return todo;
67 | });
68 |
69 | toggledTodoList.sort(sortTodo);
70 |
71 | setTodoList(toggledTodoList);
72 | }
73 |
74 | return (
75 |
76 | React Todo List
77 | setCurrentTodoItem(event.target.value)}
82 | />
83 |
84 |
97 |
98 | );
99 | }
100 |
101 | const appEl = document.querySelector("#app");
102 | const root = ReactDOM.createRoot(appEl);
103 |
104 | root.render();
105 |
--------------------------------------------------------------------------------
/part2/weather-app/final-05/src/components/Forecast.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | List,
3 | ListItem,
4 | ListItemAvatar,
5 | ListItemText,
6 | Link,
7 | Breadcrumbs,
8 | Typography,
9 | } from "@mui/material";
10 | import Avatar from "@mui/material/Avatar";
11 | import { useEffect } from "react";
12 | import { getForecastWeather } from "../services/apiWeather";
13 |
14 | // Weather forecast
15 | // https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}
16 |
17 | function Forecast({ setIsHome, position }) {
18 | const weatherForecasts = [
19 | {
20 | id: 1,
21 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
22 | min: "20",
23 | max: "30",
24 | weather: "Rain",
25 | date: "2024-04-03",
26 | },
27 | {
28 | id: 2,
29 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
30 | min: "20",
31 | max: "30",
32 | weather: "Rain",
33 | date: "2024-04-03",
34 | },
35 | {
36 | id: 3,
37 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
38 | min: "20",
39 | max: "30",
40 | weather: "Rain",
41 | date: "2024-04-03",
42 | },
43 | {
44 | id: 4,
45 | weatherIcon: "https://openweathermap.org/img/wn/10n@2x.png",
46 | min: "20",
47 | max: "30",
48 | weather: "Rain",
49 | date: "2024-04-03",
50 | },
51 | ];
52 |
53 | async function loadForecastData() {
54 | const weatherData = await getForecastWeather(
55 | position.latitude,
56 | position.longitude
57 | );
58 |
59 | console.log(weatherData);
60 | }
61 |
62 | useEffect(() => {
63 | loadForecastData();
64 | }, []);
65 |
66 | return (
67 | <>
68 |
69 | setIsHome(true)}>
70 | Home
71 |
72 |
73 | Forecast
74 |
75 |
76 |
77 |
78 | {weatherForecasts.map((weatherForecast) => (
79 |
80 |
81 |
82 |
83 |
84 |
85 |
89 |
90 | {Math.floor(weatherForecast.min)}°/
91 | {Math.ceil(weatherForecast.max)}
92 | °
93 |
94 |
95 | ))}
96 |
97 | >
98 | );
99 | }
100 | export default Forecast;
101 |
--------------------------------------------------------------------------------
/part2/weather-app/final-06/src/components/Forecast.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | List,
3 | ListItem,
4 | ListItemAvatar,
5 | ListItemText,
6 | Link,
7 | Breadcrumbs,
8 | } from "@mui/material";
9 | import Avatar from "@mui/material/Avatar";
10 | import { useEffect, useState } from "react";
11 | import { getForecastWeather } from "../services/apiWeather";
12 |
13 | // Weather forecast
14 | // https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}
15 |
16 | function Forecast({ setIsHome, position }) {
17 | const [weatherForecastList, setWeatherForecastList] = useState([]);
18 |
19 | function formatDay(dateStr) {
20 | return new Intl.DateTimeFormat("en", {
21 | weekday: "short",
22 | }).format(new Date(dateStr));
23 | }
24 |
25 | async function loadForecastData() {
26 | const weatherData = await getForecastWeather(
27 | position.latitude,
28 | position.longitude
29 | );
30 |
31 | const weatherDataList = weatherData.list;
32 |
33 | const filteredForecastList = weatherDataList
34 | .map((weatherData) => {
35 | const weather = weatherData.weather[0];
36 |
37 | return {
38 | weatherIcon: `https://openweathermap.org/img/wn/${weather.icon}@2x.png`,
39 | weather: weather.main,
40 | min: weatherData.main.temp_max,
41 | max: weatherData.main.temp_min,
42 | date: weatherData.dt_txt,
43 | };
44 | })
45 | .filter((weatherData) => weatherData.date.includes("12:00:00"))
46 | .filter((weatherData) => {
47 | const curDate = new Date().getDate();
48 | const weatherDate = new Date(weatherData.date).getDate();
49 |
50 | return curDate !== weatherDate;
51 | });
52 |
53 | setWeatherForecastList(filteredForecastList);
54 | }
55 |
56 | useEffect(() => {
57 | loadForecastData();
58 | }, []);
59 |
60 | return (
61 | <>
62 |
63 | setIsHome(true)}>
64 | Home
65 |
66 |
67 | Forecast
68 |
69 |
70 |
71 |
72 | {weatherForecastList.map((weatherForecast, idx) => (
73 |
74 |
75 |
76 |
77 |
78 |
79 |
83 |
84 | {Math.floor(weatherForecast.min)}°/
85 | {Math.ceil(weatherForecast.max)}
86 | °
87 |
88 |
89 | ))}
90 |
91 | >
92 | );
93 | }
94 | export default Forecast;
95 |
--------------------------------------------------------------------------------
/part1/lecture-04/final/main.js:
--------------------------------------------------------------------------------
1 | function MyApp() {
2 | const cities = [
3 | {
4 | name: "New York",
5 | country: "USA",
6 | forecast: [
7 | { date: "2024-04-03", temperature: 15, weather: "Partly cloudy" },
8 | { date: "2024-04-04", temperature: 17, weather: "Sunny" },
9 | { date: "2024-04-05", temperature: 18, weather: "Partly cloudy" },
10 | { date: "2024-04-06", temperature: 20, weather: "Rain" },
11 | { date: "2024-04-07", temperature: 16, weather: "Thunderstorms" },
12 | { date: "2024-04-08", temperature: 14, weather: "Cloudy" },
13 | { date: "2024-04-09", temperature: 13, weather: "Partly cloudy" },
14 | ],
15 | },
16 | {
17 | name: "London",
18 | country: "UK",
19 | forecast: [
20 | { date: "2024-04-03", temperature: 12, weather: "Cloudy" },
21 | { date: "2024-04-04", temperature: 14, weather: "Rain" },
22 | { date: "2024-04-05", temperature: 15, weather: "Partly cloudy" },
23 | { date: "2024-04-06", temperature: 13, weather: "Sunny" },
24 | { date: "2024-04-07", temperature: 11, weather: "Cloudy" },
25 | { date: "2024-04-08", temperature: 10, weather: "Rain" },
26 | { date: "2024-04-09", temperature: 12, weather: "Partly cloudy" },
27 | ],
28 | },
29 | {
30 | name: "Tokyo",
31 | country: "Japan",
32 | forecast: [
33 | { date: "2024-04-03", temperature: 20, weather: "Sunny" },
34 | { date: "2024-04-04", temperature: 21, weather: "Partly cloudy" },
35 | { date: "2024-04-05", temperature: 22, weather: "Cloudy" },
36 | { date: "2024-04-06", temperature: 19, weather: "Rain" },
37 | { date: "2024-04-07", temperature: 18, weather: "Partly cloudy" },
38 | { date: "2024-04-08", temperature: 17, weather: "Sunny" },
39 | { date: "2024-04-09", temperature: 20, weather: "Cloudy" },
40 | ],
41 | },
42 |
43 | {
44 | name: "Sydney",
45 | country: "Australia",
46 | forecast: [],
47 | },
48 |
49 | {
50 | name: "Beijing",
51 | country: "China",
52 | },
53 | ];
54 |
55 | return (
56 |
57 | {cities.map((city) => (
58 |
59 | {/* Country */}
60 | {city.country}
61 |
62 | {/* City */}
63 | {city.name}
64 |
65 | {city.forecast && city.forecast.length > 0 ? (
66 |
67 | {city.forecast.map((day) => (
68 | -
69 | {/* date */}
70 | {day.date}
71 | {/* temperature and weather */}
72 |
73 | temperature: {day.temperature}℃(
74 | {day.weather})
75 |
76 |
77 | ))}
78 |
79 | ) : (
80 | No Data
81 | )}
82 |
83 | ))}
84 |
85 | );
86 | }
87 |
88 | const appEl = document.querySelector("#app");
89 | const root = ReactDOM.createRoot(appEl);
90 |
91 | root.render();
92 |
--------------------------------------------------------------------------------
/part1/lecture-04/starter/main.js:
--------------------------------------------------------------------------------
1 | const cities = [
2 | {
3 | name: "New York",
4 | country: "USA",
5 | forecast: [
6 | { date: "2024-04-03", temperature: 15, weather: "Partly cloudy" },
7 | { date: "2024-04-04", temperature: 17, weather: "Sunny" },
8 | { date: "2024-04-05", temperature: 18, weather: "Partly cloudy" },
9 | { date: "2024-04-06", temperature: 20, weather: "Rain" },
10 | { date: "2024-04-07", temperature: 16, weather: "Thunderstorms" },
11 | { date: "2024-04-08", temperature: 14, weather: "Cloudy" },
12 | { date: "2024-04-09", temperature: 13, weather: "Partly cloudy" },
13 | ],
14 | },
15 | {
16 | name: "London",
17 | country: "UK",
18 | forecast: [
19 | { date: "2024-04-03", temperature: 12, weather: "Cloudy" },
20 | { date: "2024-04-04", temperature: 14, weather: "Rain" },
21 | { date: "2024-04-05", temperature: 15, weather: "Partly cloudy" },
22 | { date: "2024-04-06", temperature: 13, weather: "Sunny" },
23 | { date: "2024-04-07", temperature: 11, weather: "Cloudy" },
24 | { date: "2024-04-08", temperature: 10, weather: "Rain" },
25 | { date: "2024-04-09", temperature: 12, weather: "Partly cloudy" },
26 | ],
27 | },
28 | {
29 | name: "Tokyo",
30 | country: "Japan",
31 | forecast: [
32 | { date: "2024-04-03", temperature: 20, weather: "Sunny" },
33 | { date: "2024-04-04", temperature: 21, weather: "Partly cloudy" },
34 | { date: "2024-04-05", temperature: 22, weather: "Cloudy" },
35 | { date: "2024-04-06", temperature: 19, weather: "Rain" },
36 | { date: "2024-04-07", temperature: 18, weather: "Partly cloudy" },
37 | { date: "2024-04-08", temperature: 17, weather: "Sunny" },
38 | { date: "2024-04-09", temperature: 20, weather: "Cloudy" },
39 | ],
40 | },
41 |
42 | {
43 | name: "Sydney",
44 | country: "Australia",
45 | forecast: [],
46 | },
47 |
48 | {
49 | name: "Beijing",
50 | country: "China",
51 | },
52 | ];
53 |
54 | function MyApp() {
55 | return (
56 |
57 |
58 | UK
59 | London
60 |
61 |
62 | -
63 | {new Date().toLocaleDateString()}
64 | temperature: 20℃(Sunny)
65 |
66 | -
67 | {new Date().toLocaleDateString()}
68 | temperature: 19℃(Cloudy)
69 |
70 | -
71 | {new Date().toLocaleDateString()}
72 | temperature: 12℃(Rain)
73 |
74 |
75 |
76 |
77 | UK
78 | London
79 |
80 |
81 | -
82 | {new Date().toLocaleDateString()}
83 | temperature: 20℃(Sunny)
84 |
85 | -
86 | {new Date().toLocaleDateString()}
87 | temperature: 19℃(Cloudy)
88 |
89 | -
90 | {new Date().toLocaleDateString()}
91 | temperature: 12℃(Rain)
92 |
93 |
94 |
95 |
96 | CN
97 | Beijing
98 |
99 | Can't find any data
100 |
101 |
102 | );
103 | }
104 |
105 | const appEl = document.querySelector("#app");
106 | const root = ReactDOM.createRoot(appEl);
107 |
108 | root.render();
109 |
--------------------------------------------------------------------------------
/part2/challenge-03/final/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/part2/lecture-03/final/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------