Skip to content

Commit 18dd5bb

Browse files
Update to v2.0
* Implement password-reset request and function. * UI Improvements Signed-off-by: pkm774 <mprabhat774@gmail.com>
1 parent 1c013eb commit 18dd5bb

23 files changed

+695
-34
lines changed

README.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,36 @@
22

33
A **Distinctive** and **stylish** Web application for article sharing based on **PERN** Stack. Developed with the use of **React in FrontEnd** and **Node.js, ExpressJs and PostgreSQL in BackEnd**.
44

5+
## Deployment
6+
7+
Deployed Website (V 2.0): [https://sparknest.run.place/](https://sparknest.run.place/)
8+
Deployed on AWS.
9+
510
## Features
6-
Using this website a user can :
11+
Using this website a **user** can :
712

8-
* Login, Signup, Google Login/Signup
9-
* Edit, Update, Delete user profile
10-
* Create, Edit, Update, Delete created article
11-
* Add, Reply, Delete comments
13+
* Login, Signup, Google Login/Signup.
14+
* Reset forgot password using email.
15+
* Edit, Update, Delete user profile.
16+
* Create, Edit, Update, Delete created article.
17+
* Add, Reply, Delete comments.
1218
* Save and Like articles -> shown in profile section.
1319
* Contact and send message to SparkNest team.
1420

15-
CRUD on user, articles and comments are performed on his authorized space only.
21+
Create, Read, Update and Delete on user, articles and comments are performed on user's authorized space only.
1622

1723
## Technologies used
1824

1925
* **React JS**, CSS, **Bootstrap V5.3**
20-
* Javascript, **Quill**
26+
* **Javascript**, **Quill**
2127
* **Express.js**, **Nodejs, npm**
2228
* REST APIs: **GET**, **POST**, **PATCH**, **DELETE**
2329
* **PostgresSQL RDBMS**
24-
* **Login/LogOut & Register using bcrypt**
25-
* **Local and Google strategy for Login and Sign Up**
26-
* **Session and Cookie using Passport**
30+
* **Login/LogOut & Register** using **express-session**
31+
* **Password hashing** using **bcrypt**
32+
* **Local and Google strategy** for **Login** and **Sign Up**
33+
* **Session and Cookie** using **Passport**
34+
* **Mailing and Password-reset** using **Nodemailer**
2735

2836
![blogging_mania](./images/1_home.png)
2937

client/src/App.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import Contact from "./pages/Contact";
66
import Index from "./pages/Index";
77
import Login from "./pages/Login";
88
import Signup from "./pages/Signup";
9+
import ForgotPassword from "./pages/components/passwords/ForgotPassword";
10+
import ResetMain from "./pages/components/passwords/ResetMain";
911
import Profile from "./pages/Profile";
1012
import GoogleLogin from "./pages/components/GoogleLogin";
1113
import Details from "./pages/components/Details";
@@ -33,6 +35,10 @@ function App() {
3335
<Route exact path="/article/update/publish" element={<UpdatePublish />} />
3436
<Route exact path="/session/new" element={<Login />} />
3537
<Route exact path="/signup/new" element={<Signup />} />
38+
39+
<Route exact path="/password/request" element={<ForgotPassword />} />
40+
<Route exact path="/password/reset/:email/:token" element={<ResetMain />} />
41+
3642
<Route exact path="/google/login" element={<GoogleLogin />} />
3743
<Route exact path="/profile" element={<Profile />} />
3844
<Route exact path="/profile/details" element={<Details />} />

client/src/api/SERVICESAPI.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,28 @@ export const sendMessage = (body) => {
77
"Content-Type": "application/json",
88
},
99
});
10-
};
10+
};
11+
12+
export const resetPassword = (email) => {
13+
return axios.post(`${WEB_URL}/password/request/email`, email, {
14+
headers: {
15+
"Content-Type": "application/json",
16+
},
17+
});
18+
};
19+
20+
export const verifyToken = (body) => {
21+
return axios.post(`${WEB_URL}/password/verify`, body, {
22+
headers: {
23+
"Content-Type": "application/json",
24+
},
25+
});
26+
}
27+
28+
export const patchPasswords = (body) => {
29+
return axios.post(`${WEB_URL}/password/new`, body, {
30+
headers: {
31+
"Content-Type": "application/json",
32+
},
33+
});
34+
}

client/src/assets/styles/styles.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6743,6 +6743,18 @@ div.delete-modal-footer {
67436743
justify-content: center;
67446744
}
67456745

6746+
/* -------------------------------------------------------------------
6747+
* Message Box:
6748+
* SparkNest
6749+
* ------------------------------------------------------------------- */
6750+
6751+
div.alert-box.alert-box--success p,
6752+
div.alert-box.alert-box--error p,
6753+
div.alert-box.alert-box--info p,
6754+
div.alert-box.alert-box--info p {
6755+
margin-bottom: 0;
6756+
}
6757+
67466758
/* -------------------------------------------------------------------
67476759
* Custom styling:
67486760
* SparkNest

client/src/main.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { createRoot } from 'react-dom/client'
2-
import App from './App.jsx'
1+
import { createRoot } from "react-dom/client";
2+
import { StrictMode } from "react";
3+
import App from "./App.jsx";
34

4-
createRoot(document.getElementById('root')).render(<App />);
5+
createRoot(document.getElementById("root")).render(
6+
<StrictMode>
7+
<App />
8+
</StrictMode>
9+
);

client/src/pages/Login.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ const Login = () => {
209209
</span>
210210
</label>
211211
<label className="u-add-bottom" style={{ margin: "0 0" }}>
212-
<a className="label-text" href="/forgot-password">
212+
<a className="label-text" href="/password/request">
213213
Forgot password?
214214
</a>
215215
</label>

client/src/pages/components/create/PublishArticle.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ const PublishArticle = () => {
198198
return (
199199
<>
200200
<Helmet>
201-
<title>Create Article</title>
201+
<title>Publish Article</title>
202202
</Helmet>
203203
<PreLoader />
204204
<div id="page" className="s-pagewrap">

client/src/pages/components/messageBox/ErrorMessage.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const ErrorMessage = ({ isError, errorMssg }) => {
44
return (
55
<>
66
{isError && (
7-
<div className="alert-box alert-box--error" style={{ height: "68px" }}>
7+
<div className="alert-box alert-box--error">
88
<p>{errorMssg}</p>
99
</div>
1010
)}

client/src/pages/components/messageBox/SuccessMessage.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const SuccessMessage = ({ isSuccess, successMssg }) => {
1313
return (
1414
<>
1515
{isSuccess && (
16-
<div className="alert-box alert-box--success" style={{ height: "68px" }}>
16+
<div className="alert-box alert-box--success">
1717
<p>{successMssg}</p>
1818
<span className="alert-box__close" onClick={handleClose}></span>
1919
</div>
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { useState, useEffect } from "react";
2+
import { Helmet } from "react-helmet";
3+
4+
import PreLoader from "../PreLoader";
5+
import SuccessMessage from "../messageBox/SuccessMessage";
6+
7+
import { resetPassword } from "../../../api/SERVICESAPI";
8+
9+
const ForgotPassword = () => {
10+
const [email, setEmail] = useState("");
11+
const [isSuccess, setIsSuccess] = useState(false);
12+
const [mssg, setMssg] = useState("");
13+
const [countdown, setCountdown] = useState(0);
14+
const countDownTime = 45;
15+
16+
const handleChange = (e) => {
17+
setEmail(e.target.value);
18+
};
19+
20+
const handleSubmit = async (e) => {
21+
e.preventDefault();
22+
23+
if (!email) return;
24+
25+
const body = {
26+
email: email,
27+
};
28+
29+
try {
30+
const response = await resetPassword(body);
31+
if (response.status == 200) {
32+
setMssg(response.data.message);
33+
setIsSuccess(true);
34+
setEmail("");
35+
setCountdown(countDownTime);
36+
}
37+
} catch (error) {
38+
console.log(error.response);
39+
}
40+
};
41+
42+
useEffect(() => {
43+
if (countdown > 0) {
44+
const timer = setTimeout(() => {
45+
setCountdown(countdown - 1);
46+
}, 1000);
47+
48+
return () => clearTimeout(timer);
49+
}
50+
if (countdown < 1) {
51+
setIsSuccess(false);
52+
}
53+
}, [countdown]);
54+
55+
return (
56+
<>
57+
<Helmet>
58+
<title>Forgot Pasword</title>
59+
</Helmet>
60+
<PreLoader />
61+
<div id="page" className="s-pagewrap">
62+
<section
63+
id="content"
64+
className="s-content"
65+
style={{ paddingTop: "130px" }}
66+
>
67+
<div className="row d-flex justify-content-center mb-lg-5">
68+
<div
69+
className="column lg-6 tab-12"
70+
style={{ paddingBottom: "50px" }}
71+
>
72+
<SuccessMessage isSuccess={isSuccess} successMssg={mssg} />
73+
{countdown < 1 && (
74+
<p>
75+
<u>Request a password reset link to your email</u>
76+
</p>
77+
)}
78+
{countdown > 0 && (
79+
<p>
80+
Please wait <b>{countdown}</b> seconds before requesting a new
81+
link.
82+
</p>
83+
)}
84+
<form onSubmit={handleSubmit}>
85+
<div className="form-outline mb-4">
86+
<label htmlFor="formEmail" style={{ marginBottom: "8px" }}>
87+
Enter Your Email
88+
</label>
89+
<input
90+
className="u-fullwidth"
91+
type="email"
92+
id="formEmail"
93+
name="email"
94+
placeholder="your@email.com"
95+
required=""
96+
value={email}
97+
onChange={handleChange}
98+
/>
99+
</div>
100+
<button
101+
type="submit"
102+
className="btn--primary u-fullwidth mb-5"
103+
disabled={countdown > 0}
104+
>
105+
{countdown > 0 ? `Wait ${countdown}s` : "Submit"}
106+
</button>
107+
</form>
108+
</div>
109+
</div>
110+
</section>
111+
</div>
112+
</>
113+
);
114+
};
115+
116+
export default ForgotPassword;

0 commit comments

Comments
 (0)