Skip to content

Commit 190f517

Browse files
committed
Checkpoint 100xDevs-hkirat#3 => State management using reciol
1 parent 664d013 commit 190f517

File tree

11 files changed

+220
-57
lines changed

11 files changed

+220
-57
lines changed

admin-client/src/App.jsx

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,75 @@ import AddCourse from "./components/AddCourse.jsx";
66
import Courses from "./components/Courses";
77
import Course from "./components/Course";
88
import {Landing} from "./components/Landing.jsx";
9-
import {useState, useEffect} from "react";
9+
import { userState } from "./store/atoms/user.js";
10+
import {
11+
RecoilRoot,
12+
useSetRecoilState
13+
} from 'recoil';
1014
import axios from "axios";
1115
import {BASE_URL} from "./config.js";
16+
import {useEffect} from "react";
1217

1318
function App() {
14-
const [userEmail, setUserEmail] = useState(null)
19+
return (
20+
<RecoilRoot>
21+
<div style={{width: "100vw",
22+
height: "100vh",
23+
backgroundColor: "#eeeeee"}}
24+
>
25+
<Router>
26+
<Appbar />
27+
<InitUser />
28+
<Routes>
29+
<Route path={"/addcourse"} element={<AddCourse />} />
30+
<Route path={"/course/:courseId"} element={<Course />} />
31+
<Route path={"/courses"} element={<Courses />} />
32+
<Route path={"/signin"} element={<Signin />} />
33+
<Route path={"/signup"} element={<Signup />} />
34+
<Route path={"/"} element={<Landing />} />
35+
</Routes>
36+
</Router>
37+
</div>
38+
</RecoilRoot>
39+
);
40+
}
41+
1542

43+
function InitUser() {
44+
const setUser = useSetRecoilState(userState);
1645
const init = async() => {
17-
const response = await axios.get(`${BASE_URL}/admin/me`, {
18-
headers: {
19-
"Authorization": "Bearer " + localStorage.getItem("token")
46+
try {
47+
const response = await axios.get(`${BASE_URL}/admin/me`, {
48+
headers: {
49+
"Authorization": "Bearer " + localStorage.getItem("token")
50+
}
51+
})
52+
53+
if (response.data.username) {
54+
setUser({
55+
isLoading: false,
56+
userEmail: response.data.username
57+
})
58+
} else {
59+
setUser({
60+
isLoading: false,
61+
userEmail: null
62+
})
2063
}
21-
})
64+
} catch (e) {
2265

23-
if (response.data.username) {
24-
setUserEmail(response.data.username)
66+
setUser({
67+
isLoading: false,
68+
userEmail: null
69+
})
2570
}
2671
};
2772

2873
useEffect(() => {
2974
init();
3075
}, []);
31-
return (
32-
<div style={{width: "100vw",
33-
height: "100vh",
34-
backgroundColor: "#eeeeee"}}
35-
>
36-
<Router>
37-
<Appbar userEmail={userEmail} setUserEmail={setUserEmail}/>
38-
<Routes>
39-
<Route path={"/addcourse"} element={<AddCourse />} />
40-
<Route path={"/course/:courseId"} element={<Course />} />
41-
<Route path={"/courses"} element={<Courses />} />
42-
<Route path={"/signin"} element={<Signin setUserEmail={setUserEmail} />} />
43-
<Route path={"/signup"} element={<Signup setUserEmail={setUserEmail} />} />
44-
<Route path={"/"} element={<Landing userEmail={userEmail} />} />
45-
</Routes>
46-
</Router>
47-
48-
</div>
49-
);
76+
77+
return <></>
5078
}
5179

5280
export default App;

admin-client/src/components/Appbar.jsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import {Typography} from "@mui/material";
22
import Button from "@mui/material/Button";
3-
import {useNavigate} from "react-router-dom";
3+
import { useNavigate } from "react-router-dom";
4+
import { isUserLoading } from "../store/selectors/isUserLoading";
5+
import {useSetRecoilState, useRecoilValue} from "recoil";
6+
import { userState } from "../store/atoms/user.js";
7+
import { userEmailState } from "../store/selectors/userEmail"
48

5-
function Appbar({userEmail, setUserEmail}) {
9+
function Appbar({}) {
610
const navigate = useNavigate()
11+
const userLoading = useRecoilValue(isUserLoading);
12+
const userEmail = useRecoilValue(userEmailState);
13+
const setUser = useSetRecoilState(userState);
14+
15+
if (userLoading) {
16+
return <></>
17+
}
718

819
if (userEmail) {
920
return <div style={{
@@ -40,7 +51,10 @@ function Appbar({userEmail, setUserEmail}) {
4051
variant={"contained"}
4152
onClick={() => {
4253
localStorage.setItem("token", null);
43-
setUserEmail(null);
54+
setUser({
55+
isLoading: false,
56+
userEmail: null
57+
})
4458
}}
4559
>Logout</Button>
4660
</div>

admin-client/src/components/Course.jsx

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,48 @@ import { Typography, TextField, Button } from "@mui/material";
55
import axios from "axios";
66
import {Loading} from "./Loading";
77
import { BASE_URL } from "../config.js";
8+
import { courseState } from "../store/atoms/course";
9+
import {useRecoilState, useRecoilValue, useSetRecoilState} from "recoil";
10+
import { courseTitle, coursePrice, isCourseLoading, courseImage } from "../store/selectors/course";
811

912
function Course() {
1013
let { courseId } = useParams();
11-
const [course, setCourse] = useState(null);
12-
14+
const setCourse = useSetRecoilState(courseState);
15+
const courseLoading = useRecoilValue(isCourseLoading);
16+
1317
useEffect(() => {
1418
axios.get(`${BASE_URL}/admin/course/${courseId}`, {
1519
method: "GET",
1620
headers: {
1721
"Authorization": "Bearer " + localStorage.getItem("token")
1822
}
1923
}).then(res => {
20-
setCourse(res.data.course);
24+
setCourse({isLoading: false, course: res.data.course});
25+
})
26+
.catch(e => {
27+
setCourse({isLoading: false, course: null});
2128
});
2229
}, []);
2330

24-
if (!course) {
31+
if (courseLoading) {
2532
return <Loading />
2633
}
2734

2835
return <div>
29-
<GrayTopper title={course.title}/>
36+
<GrayTopper />
3037
<Grid container>
3138
<Grid item lg={8} md={12} sm={12}>
32-
<UpdateCard course={course} setCourse={setCourse} />
39+
<UpdateCard />
3340
</Grid>
3441
<Grid item lg={4} md={12} sm={12}>
35-
<CourseCard course={course} />
42+
<CourseCard />
3643
</Grid>
3744
</Grid>
3845
</div>
3946
}
4047

41-
function GrayTopper({title}) {
48+
function GrayTopper() {
49+
const title = useRecoilValue(courseTitle);
4250
return <div style={{height: 250, background: "#212121", top: 0, width: "100vw", zIndex: 0, marginBottom: -250}}>
4351
<div style={{ height: 250, display: "flex", justifyContent: "center", flexDirection: "column"}}>
4452
<div>
@@ -50,11 +58,13 @@ function GrayTopper({title}) {
5058
</div>
5159
}
5260

53-
function UpdateCard({course, setCourse}) {
54-
const [title, setTitle] = useState(course.title);
55-
const [description, setDescription] = useState(course.description);
56-
const [image, setImage] = useState(course.imageLink);
57-
const [price, setPrice] = useState(course.price);
61+
function UpdateCard() {
62+
const [courseDetails, setCourse] = useRecoilState(courseState);
63+
64+
const [title, setTitle] = useState(courseDetails.course.title);
65+
const [description, setDescription] = useState(courseDetails.course.description);
66+
const [image, setImage] = useState(courseDetails.course.imageLink);
67+
const [price, setPrice] = useState(courseDetails.course.price);
5868

5969
return <div style={{display: "flex", justifyContent: "center"}}>
6070
<Card varint={"outlined"} style={{maxWidth: 600, marginTop: 200}}>
@@ -106,7 +116,7 @@ function UpdateCard({course, setCourse}) {
106116
<Button
107117
variant="contained"
108118
onClick={async () => {
109-
axios.put(`${BASE_URL}/admin/courses/` + course._id, {
119+
axios.put(`${BASE_URL}/admin/courses/` + courseDetails.course._id, {
110120
title: title,
111121
description: description,
112122
imageLink: image,
@@ -119,13 +129,13 @@ function UpdateCard({course, setCourse}) {
119129
}
120130
});
121131
let updatedCourse = {
122-
_id: course._id,
132+
_id: courseDetails.course._id,
123133
title: title,
124134
description: description,
125135
imageLink: image,
126136
price
127137
};
128-
setCourse(updatedCourse);
138+
setCourse({course: updatedCourse, isLoading: false});
129139
}}
130140
> Update course</Button>
131141
</div>
@@ -134,7 +144,9 @@ function UpdateCard({course, setCourse}) {
134144
}
135145

136146
function CourseCard(props) {
137-
const course = props.course;
147+
const title = useRecoilValue(courseTitle);
148+
const imageLink = useRecoilValue(courseImage);
149+
138150
return <div style={{display: "flex", marginTop: 50, justifyContent: "center", width: "100%"}}>
139151
<Card style={{
140152
margin: 10,
@@ -145,14 +157,14 @@ function CourseCard(props) {
145157
paddingBottom: 15,
146158
zIndex: 2
147159
}}>
148-
<img src={course.imageLink} style={{width: 350}} ></img>
160+
<img src={imageLink} style={{width: 350}} ></img>
149161
<div style={{marginLeft: 10}}>
150-
<Typography variant="h5">{course.title}</Typography>
162+
<Typography variant="h5">{title}</Typography>
151163
<Typography variant="subtitle2" style={{color: "gray"}}>
152164
Price
153165
</Typography>
154166
<Typography variant="subtitle1">
155-
<b>Rs {course.price} </b>
167+
<b>Rs {price} </b>
156168
</Typography>
157169
</div>
158170
</Card>

admin-client/src/components/Landing.jsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import {Grid, Typography} from "@mui/material";
22
import Button from "@mui/material/Button";
33
import {useNavigate} from "react-router-dom";
4+
import {useRecoilValue} from "recoil";
5+
import { userEmailState } from "../store/selectors/userEmail"
6+
import {isUserLoading} from "../store/selectors/isUserLoading.js";
47

5-
6-
export const Landing = ({userEmail}) => {
8+
export const Landing = () => {
79
const navigate = useNavigate()
10+
const userEmail = useRecoilValue(userEmailState);
11+
const userLoading = useRecoilValue(isUserLoading);
812
return <div>
913
<Grid container style={{padding: "5vw"}}>
1014
<Grid item xs={12} md={6} lg={6}>
@@ -15,7 +19,7 @@ export const Landing = ({userEmail}) => {
1519
<Typography variant={"h5"}>
1620
A place to learn, earn and grow
1721
</Typography>
18-
{!userEmail && <div style={{display: "flex", marginTop: 20}}>
22+
{!userLoading && !userEmail && <div style={{display: "flex", marginTop: 20}}>
1923
<div style={{marginRight: 10}}>
2024
<Button
2125
size={"large"}

admin-client/src/components/Signin.jsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import {Card, Typography} from "@mui/material";
44
import {useState} from "react";
55
import axios from "axios";
66
import {useNavigate} from "react-router-dom";
7+
import {useSetRecoilState} from "recoil";
8+
import {userState} from "../store/atoms/user.js";
79

810
function Signin() {
911
const [email, setEmail] = useState("")
1012
const [password, setPassword] = useState("")
1113
const navigate = useNavigate()
14+
const setUser = useSetRecoilState(userState);
1215

1316
return <div>
1417
<div style={{
@@ -57,10 +60,13 @@ function Signin() {
5760
}
5861
});
5962
const data = res.data;
60-
63+
6164
localStorage.setItem("token", data.token);
6265
// window.location = "/"
63-
setUserEmail(email)
66+
setUser({
67+
userEmail: email,
68+
isLoading: false
69+
})
6470
navigate("/courses")
6571
}}
6672

admin-client/src/components/Signup.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import {useState} from "react";
55
import axios from "axios";
66
import { BASE_URL } from "../config.js";
77
import {useNavigate} from "react-router-dom";
8+
import {useSetRecoilState} from "recoil";
9+
import {userState} from "../store/atoms/user.js";
810

9-
function Signup({setUserEmail}) {
11+
function Signup() {
1012
const [email, setEmail] = useState("")
1113
const [password, setPassword] = useState("")
1214
const navigate = useNavigate()
15+
const setUser = useSetRecoilState(userState);
1316

1417
return <div>
1518
<div style={{
@@ -55,7 +58,7 @@ function Signup({setUserEmail}) {
5558
let data = response.data;
5659
localStorage.setItem("token", data.token);
5760
// window.location = "/"
58-
setUserEmail(email)
61+
setUser({userEmail: email, isLoading: false})
5962
navigate("/courses")
6063
}}
6164

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {atom} from "recoil";
2+
3+
export const courseState = atom({
4+
key: 'courseState',
5+
default: {
6+
isLoading: true,
7+
course: null
8+
},
9+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {atom} from "recoil";
2+
3+
export const userState = atom({
4+
key: 'userState',
5+
default: {
6+
isLoading: true,
7+
userEmail: null
8+
},
9+
});

0 commit comments

Comments
 (0)