Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADHD CONNECT - FINAL PROJECT _ BMM #52

Open
wants to merge 72 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
6f53dbc
set up dev environment
Maria-Manuela May 27, 2024
0bc3501
home-page
Maria-Manuela May 28, 2024
fc9ee50
home page in progress
Maria-Manuela May 28, 2024
16454a4
utilities + mobile screen styling
Maria-Manuela May 29, 2024
9999fd7
deployment test
Maria-Manuela May 29, 2024
9f7494f
backend
Maria-Manuela May 30, 2024
790c910
refactor, profile route added
Maria-Manuela May 30, 2024
a8bdbc2
backend changes
Maria-Manuela May 30, 2024
be5e9b8
fetch profile route added
Maria-Manuela May 30, 2024
cc5f634
debugging. backend full operational
Maria-Manuela May 31, 2024
629ed79
implementing BE in FE, in progress
Maria-Manuela May 31, 2024
769dbb6
debug
Maria-Manuela Jun 1, 2024
786f52a
debugg
Maria-Manuela Jun 1, 2024
af9706d
allow acces for the local testing
Maria-Manuela Jun 1, 2024
94291cd
adjustments
Maria-Manuela Jun 1, 2024
f270a4c
route for hadling logged-in user
Maria-Manuela Jun 1, 2024
3f5ae00
refactor
Maria-Manuela Jun 1, 2024
f0989fb
fix CORS issue
Maria-Manuela Jun 1, 2024
91aae83
working on errors
Maria-Manuela Jun 1, 2024
cd2a7e5
debug profile route
Maria-Manuela Jun 2, 2024
6501055
model update
Maria-Manuela Jun 2, 2024
469c037
event page
Maria-Manuela Jun 4, 2024
3861f24
footer + last changes
Maria-Manuela Jun 4, 2024
55ac6b7
updates + remove picture upload option
Maria-Manuela Jun 5, 2024
e1c17b4
profile update in progress...
Maria-Manuela Jun 5, 2024
d899af4
debug
Maria-Manuela Jun 6, 2024
cf21155
refactor and modal context added
Maria-Manuela Jun 6, 2024
9ade299
ABout Us Page
Maria-Manuela Jun 7, 2024
41076d7
guidelines page
Maria-Manuela Jun 7, 2024
8d0b9ee
refactor
Maria-Manuela Jun 8, 2024
ff80bee
flipping card animation
Maria-Manuela Jun 9, 2024
455cfd2
fliping card functional done
Maria-Manuela Jun 9, 2024
1d1b5e0
FOM Page, event card, sign up
Maria-Manuela Jun 9, 2024
efa5589
refactor for auth +debug
Maria-Manuela Jun 10, 2024
547962e
sign-up login logic functional
Maria-Manuela Jun 10, 2024
0153c0d
update
Maria-Manuela Jun 11, 2024
482db9e
update
Maria-Manuela Jun 11, 2024
3071123
netlify config
Maria-Manuela Jun 11, 2024
d5a1a54
profile update function-done
Maria-Manuela Jun 11, 2024
cff8b1c
profile page, done
Maria-Manuela Jun 11, 2024
cc6fce7
link to profile added
Maria-Manuela Jun 11, 2024
407fa20
update local server, modal added
Maria-Manuela Jun 12, 2024
9e7fd9d
small adjustments
Maria-Manuela Jun 12, 2024
1cf4181
adjust BE profile
Maria-Manuela Jun 12, 2024
791a945
log out succes
Maria-Manuela Jun 12, 2024
606a2f6
Login added to the menu
Maria-Manuela Jun 12, 2024
b61aefb
testing
Maria-Manuela Jun 12, 2024
a1bfad2
adjustments flipping card
Maria-Manuela Jun 13, 2024
83347fc
FE -last changes
Maria-Manuela Jun 13, 2024
7776847
clean up BE
Maria-Manuela Jun 13, 2024
1e8df4a
loading state
Maria-Manuela Jun 13, 2024
364222a
update
Maria-Manuela Jun 14, 2024
51e5819
optimization
Maria-Manuela Jun 16, 2024
111a625
extra features
Maria-Manuela Jun 16, 2024
bf1bc40
senction about us adjusted
Maria-Manuela Jun 16, 2024
ff6de4a
testimonials
Maria-Manuela Jun 16, 2024
eed492b
testimonials features done
Maria-Manuela Jun 16, 2024
07cf420
local access
Maria-Manuela Jun 17, 2024
2c1e7b0
BE
Maria-Manuela Jun 17, 2024
55ce067
error validation
Maria-Manuela Jun 17, 2024
da772be
error validation for signup and login. readMe
Maria-Manuela Jun 17, 2024
6638e4d
pic optimize
Maria-Manuela Jun 18, 2024
79a3232
optimize picture
Maria-Manuela Jun 18, 2024
89bae0d
adjustments
Maria-Manuela Jun 19, 2024
8f65772
organized components
Maria-Manuela Jun 19, 2024
39d1fa7
name changed
Maria-Manuela Jun 19, 2024
061764e
fix inline style
Maria-Manuela Jun 20, 2024
78c7e52
ReadMe + remove unused file + naming convention
Maria-Manuela Jun 24, 2024
9673fe5
local
Maria-Manuela Jun 24, 2024
7152830
naming convention
Maria-Manuela Jun 24, 2024
d2739b4
login modal adjusted
Maria-Manuela Jun 26, 2024
cb869b0
faq
Maria-Manuela Jun 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# Backend part of Final Project

This project includes the packages and babel setup for an express server, and is just meant to make things a little simpler to get up and running with.
The backend serves as the core logic and data management system for the application. It provides the necessary endpoints to handle user authentication, registration and profile management.

## Getting Started
## Process

1. Install the required dependencies using `npm install`.
2. Start the development server using `npm run dev`.
Technologies Used:

Node.js
Express.js
MongoDB
Mongoose
JWT
Passport.js
Multer

## LINK

https://project-final-rmn2.onrender.com
41 changes: 41 additions & 0 deletions backend/config/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import User from "../models/User";

passport.use(
new LocalStrategy(
{ usernameField: "username" },
async (username, password, done) => {
try {
const user = await User.findOne({ username });
if (!user) {
return done(null, false, { message: "Incorrect username." });
}

const isMatch = await user.comparePassword(password);
if (!isMatch) {
return done(null, false, { message: "Incorrect password." });
}

return done(null, user);
} catch (error) {
return done(error);
}
}
)
);

passport.serializeUser((user, done) => {
done(null, user.id);
});

passport.deserializeUser(async (id, done) => {
try {
const user = await User.findById(id);
done(null, user);
} catch (error) {
done(error);
}
});

export default passport;
29 changes: 29 additions & 0 deletions backend/models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import mongoose from "mongoose";
import bcrypt from "bcryptjs";

const userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true, minlength: 3 },
password: { type: String, required: true, minlength: 6 },
role: { type: String, enum: ["Listener", "Seeker"], required: true },
name: { type: String },
bio: { type: String },
hobby: { type: String },
});

// Middleware to hash the password before saving
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
return next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});

// Method to compare the entered password with the hashed password
userSchema.methods.comparePassword = function (enteredPassword) {
return bcrypt.compare(enteredPassword, this.password);
};

const User = mongoose.model("User", userSchema);
export default User;
10 changes: 8 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.17.3",
"express-list-endpoints": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.4.0",
"nodemon": "^3.0.1"
"nodemon": "^3.0.1",
"passport": "^0.7.0",
"passport-local": "^1.0.0"
}
}
}
138 changes: 138 additions & 0 deletions backend/routes/authProfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import express from "express";
import jwt from "jsonwebtoken";
import passport from "../config/passport";
import User from "../models/User";

const router = express.Router();

// Function to generate JWT access token
const generateAccessToken = (userId) => {
try {
return jwt.sign({ userId }, process.env.JWT_SECRET, {
expiresIn: "24h",
});
} catch (error) {
console.error("Error generating token:", error);
throw new Error("Token generation failed");
}
};

// Middleware to athenticate the token
const authenticateToken = async (req, res, next) => {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];

if (token == null) {
return res.status(401).json({ error: "Token is missing" });
}

jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: "Token is invalid" });
}
req.user = user;
next();
});
};

// Route to register a new user
router.post("/users", async (req, res) => {
const { username, password, role } = req.body;
if (password.length < 6) {
return res
.status(400)
.json({ error: "Password must be at least 6 characters long" });
}

try {
const newUser = new User({ username, password, role });
await newUser.save();
const accessToken = generateAccessToken(newUser._id);
res.status(201).json({ id: newUser._id, accessToken });
} catch (error) {
console.error("Error registering user:", error);
if (error.code === 11000) {
res.status(400).json({ error: "Username already exists" });
} else {
res.status(500).json({ error: "Something went wrong" });
}
}
});

// Route to log in a user
router.post("/sessions", async (req, res, next) => {
passport.authenticate("local", { session: false }, (err, user, info) => {
if (err || !user) {
console.log("Error during authentication:", err);
return res
.status(400)
.json({ error: info ? info.message : "Login failed" });
}
req.login(user, { session: false }, async (err) => {
if (err) {
return res.send(err);
}
const token = generateAccessToken(user._id);
const userWithoutAccessToken = user.toObject();
delete userWithoutAccessToken.accessToken;
return res.json({ user: userWithoutAccessToken, token });
});
})(req, res, next);
});


// Route for the current session (logged-in user)
router.get("/session", authenticateToken, async (req, res) => {
try {
const user = req.user;
const loggedInUser = await User.findById(user.userId).select("-password");
if (!loggedInUser) {
return res.status(404).json({ error: "User not found" });
}
res.json(loggedInUser);
} catch (error) {
console.error("Error fetching logged-in user:", error);
res.status(500).json({ error: "Failed to fetch logged-in user" });
}
});

// Route to fetch profile
router.get("/profile", authenticateToken, async (req, res) => {
console.log("GET /profile called");
try {
const user = await User.findById(req.user.userId).select("-password");
if (!user) {
return res.status(404).json({ error: "User not found" });
}
res.json(user);
} catch (error) {
console.error("Error fetching profile:", error);
res.status(500).json({ error: "Failed to fetch profile" });
}
});

//Route to update user profile
router.put("/profile", authenticateToken, async (req, res) => {
const { name, bio, hobby } = req.body;
const updatedData = { name, bio, hobby };

try {
const user = await User.findByIdAndUpdate(req.user.userId, updatedData, {
new: true,
});
if (!user) {
return res.status(404).json({ error: "User not found" });
}
res.json(user);
} catch (error) {
console.error("Error updating profile:", error);
res.status(500).json({ error: "Failed to update profile" });
}
});

// Authenticated endpoint
router.get("/secrets", authenticateToken, (req, res) => {
res.json({ secret: "This is secret content" });
});

export default router;
40 changes: 24 additions & 16 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
import express from "express";
import cors from "cors";
import mongoose from 'mongoose'
import dotenv from "dotenv";
import express from "express";
import expressListEndpoints from "express-list-endpoints";
import mongoose from "mongoose";

const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/flowershop"
mongoose.connect(mongoUrl)
mongoose.Promise = Promise
import passport from "./config/passport";
import authProfileRoutes from "./routes/authProfile";

dotenv.config();

const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-final";
mongoose.connect(mongoUrl);
mongoose.Promise = global.Promise;

// Defines the port the app will run on. Defaults to 8080, but can be overridden
// when starting the server. Example command to overwrite PORT env variable value:
// PORT=9000 npm start
const port = process.env.PORT || 8080;
const port = process.env.PORT || 9000;
const app = express();

// Add middlewares to enable cors and json body parsing
app.use(cors());
// middlewares to enable cors and json body parsing
const corsOptions = {
origin: "https://adhd-connect.netlify.app",
credentials: true,
};

app.use(cors(corsOptions));
app.use(express.json());
app.use(passport.initialize());

app.use("/api", authProfileRoutes);

// Start defining your routes here
// http://localhost:8080/
app.get("/", (req, res) => {
res.send("Hello Technigo!");
const endpoints = expressListEndpoints(app);
res.json(endpoints);
});


// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
});
30 changes: 26 additions & 4 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
# Frontend part of Final Project

This boilerplate is designed to give you a head start in your React projects, with a focus on understanding the structure and components. As a student of Technigo, you'll find this guide helpful in navigating and utilizing the repository.
The ADHD Community Project is a web application designed to support and empower individuals with ADHD by providing a safe space and understanding community. Users can sign up as either a "Listener" or a "Seeker" to offer or receive support, respectively. The application includes features such as event listings, community guidelines, and user profiles.

## Getting Started
## About the project

1. Install the required dependencies using `npm install`.
2. Start the development server using `npm run dev`.
Developed using React for the frontend and Node.js with Express for the backend, the application integrates MongoDB for data storage and utilizes Passport.js with JWT for secure authentication. Key features include user authentication with roles (Listener and Seeker), event listings, and user profile management

Future enhancements include a live chat feature to facilitate real-time communication within the community

## Challenges

Handling Modal Popups:

Challenge: Managing the state and functionality of modal popups for login and signup forms.
Solution: Created a custom modal context using React's Context API to manage the display and state of modals. This centralized approach streamlined modal management, making it easier to maintain and extend the functionality.

API Integration:

Challenge: Integrating the frontend with the backend API and ensuring seamless data flow.
Solution: Developed a consistent API service layer to handle requests and responses between the frontend and backend. Implemented error handling and loading states to enhance user experience during data fetching operations.

Profile Management:

Challenge: Allowing users to view and update their profiles securely.
Solution: Implemented profile fetching and updating functionalities with secure token-based authentication. Ensured data validation and used React forms to allow users to edit their profiles, providing real-time feedback and updates.

## Link

https://adhd-connect.netlify.app
4 changes: 2 additions & 2 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg" href="/images/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Technigo React Vite Boiler Plate</title>
<title>ADHD Connect</title>
</head>
<body>
<div id="root"></div>
Expand Down
9 changes: 9 additions & 0 deletions frontend/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[build]
base = "frontend"
publish = "dist"
command = "npm run build"

[[redirects]]
from = "/*"
to = "/index.html"
status = 200
9 changes: 7 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.1"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
"autoprefixer": "^10.4.19",
"eslint": "^8.45.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.3",
"vite": "^4.4.5"
}
}
}
Loading