Skip to content

Commit 4e94c87

Browse files
committed
Add user profile page and API
1 parent ed271d3 commit 4e94c87

File tree

6 files changed

+193
-0
lines changed

6 files changed

+193
-0
lines changed

backend/controllers/userController.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
import User from '../models/User.js';
3+
4+
export const getUserProfile = async (req, res) => {
5+
try {
6+
const userId = req.params.id;
7+
const user = await User.findById(userId).select('-password');
8+
9+
if (!user) {
10+
return res.status(404).json({ message: 'User not found' });
11+
}
12+
13+
res.status(200).json(user);
14+
res.status(500).json({ message: 'Server error', error });
15+
}catch(err) {
16+
console.log(err);
17+
}
18+
};

backend/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ import stationRoutes from "./routes/stationRoutes.js";
3232
import trainRoutes from "./routes/trainRoutes.js";
3333
import contactUs from "./routes/contactUsRouter.js";
3434
import complaintRoutes from "./routes/complaintRoutes.js";
35+
import userRoutes from "./routes/userRoutes.js";
3536

3637
app.use("/auth", authRoutes);
3738
app.use("/api", authRoutes);
39+
app.use("/api", userRoutes);
3840
app.use("/api", complaintRoutes);
3941
app.use("/station", stationRoutes);
4042
app.use("/train", trainRoutes);

backend/routes/userRoutes.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// routes/userRoutes.js
2+
import express from 'express';
3+
import { getUserProfile } from '../controllers/userController.js';
4+
5+
const router = express.Router();
6+
7+
router.get('/profile/:id', getUserProfile);
8+
9+
export default router;

frontend/src/App.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import ComplainBox from "./Pages/ComplainBox";
3030
import Metadata from "./metadata";
3131
import SettingsPage from "./Pages/Settings";
3232
import Faq from './Pages/Faq';
33+
import ProfilePage from "./Pages/Profile";
3334

3435
function App() {
3536
return (
@@ -49,6 +50,7 @@ function App() {
4950
<Route path="/Notification" element={<NotificationPage />} />
5051
<Route path="/chatbot" element={<Chatbot />} />
5152
<Route path="/ContactUs" element={<ContactUs />} />
53+
<Route path="/profile" element={<ProfilePage />} />
5254

5355
<Route path="/GoogleTranslate" element={<GoogleTranslate />} />
5456
<Route path="/help" element={<Help />} />

frontend/src/Pages/Profile.jsx

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { useNavigate } from 'react-router-dom';
3+
import axios from 'axios';
4+
import placeholderImage from '../assets/person.jpg';
5+
6+
const ProfilePage = ({ userId }) => {
7+
const navigate = useNavigate();
8+
const [profilePicture, setProfilePicture] = useState(placeholderImage);
9+
const [userData, setUserData] = useState({});
10+
const [isEditing, setIsEditing] = useState(false);
11+
12+
// Fetch user profile data on component load
13+
useEffect(() => {
14+
const fetchUserProfile = async () => {
15+
16+
try {
17+
const response = await axios.get(`http://localhost:3000/api/profile/${userId}`);
18+
const data = response.data;
19+
setUserData(data);
20+
setProfilePicture(data.profilePicture || placeholderImage);
21+
} catch (error) {
22+
console.error('Error fetching profile:', error);
23+
}
24+
};
25+
fetchUserProfile();
26+
}, [userId]);
27+
28+
const handleLogout = () => {
29+
navigate('/login');
30+
};
31+
32+
return (
33+
<div className="w-full max-w-2xl mx-auto p-8 bg-white shadow-lg rounded-lg mt-10">
34+
<h3 className='text-3xl text-center font-bold '>User Profile</h3>
35+
{/* Profile Header */}
36+
<div className="flex items-center space-x-6">
37+
<div className="relative">
38+
<img
39+
src={profilePicture}
40+
alt="Profile"
41+
className="h-28 w-28 rounded-full object-cover border border-gray-300"
42+
/>
43+
<input
44+
type="file"
45+
className="hidden"
46+
onChange={(e) => {
47+
const file = e.target.files[0];
48+
const newProfilePicture = URL.createObjectURL(file);
49+
setProfilePicture(newProfilePicture); // Temporarily show new picture
50+
// Handle picture upload to server here
51+
}}
52+
/>
53+
</div>
54+
<div>
55+
<h2 className="text-2xl font-semibold text-gray-800">{userData.name}</h2>
56+
<p className="text-gray-500">{userData.email}</p>
57+
<p className="text-gray-500">{userData.phoneNumber}</p>
58+
</div>
59+
</div>
60+
61+
{/* Action Buttons */}
62+
<div className="flex justify-around mt-6">
63+
<button
64+
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
65+
onClick={() => setIsEditing(true)}
66+
>
67+
Edit Profile
68+
</button>
69+
<button
70+
className="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600"
71+
onClick={handleLogout}
72+
>
73+
Logout
74+
</button>
75+
</div>
76+
77+
{/* Profile Information and Stats */}
78+
<div className="mt-8 space-y-6">
79+
<div className="p-6 bg-gray-100 rounded-lg">
80+
<h3 className="text-xl font-semibold text-gray-700">About</h3>
81+
<p className="mt-2 text-gray-600">
82+
{userData.bio || "This is a placeholder bio. Update your profile to add more information."}
83+
</p>
84+
</div>
85+
86+
<div className="p-6 bg-gray-100 rounded-lg flex justify-between">
87+
<div className="flex flex-col items-center">
88+
<span className="text-2xl font-bold text-blue-600">15</span>
89+
<span className="text-gray-500">Posts</span>
90+
</div>
91+
<div className="flex flex-col items-center">
92+
<span className="text-2xl font-bold text-blue-600">24</span>
93+
<span className="text-gray-500">Reviews</span>
94+
</div>
95+
<div className="flex flex-col items-center">
96+
<span className="text-2xl font-bold text-blue-600">4.5</span>
97+
<span className="text-gray-500">Rating</span>
98+
</div>
99+
</div>
100+
</div>
101+
102+
{/* Edit Profile Form */}
103+
{isEditing && (
104+
<div className="mt-8 p-6 bg-white rounded-lg shadow-md">
105+
<h3 className="text-xl font-bold mb-4">Edit Profile</h3>
106+
<form onSubmit={(e) => e.preventDefault()} className="space-y-4">
107+
<div>
108+
<label className="block text-gray-700">Name</label>
109+
<input
110+
type="text"
111+
name="name"
112+
value={userData.name || ''}
113+
onChange={(e) => setUserData({ ...userData, name: e.target.value })}
114+
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
115+
/>
116+
</div>
117+
<div>
118+
<label className="block text-gray-700">Email</label>
119+
<input
120+
type="email"
121+
name="email"
122+
value={userData.email || ''}
123+
onChange={(e) => setUserData({ ...userData, email: e.target.value })}
124+
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
125+
/>
126+
</div>
127+
<div>
128+
<label className="block text-gray-700">Phone Number</label>
129+
<input
130+
type="text"
131+
name="phoneNumber"
132+
value={userData.phoneNumber || ''}
133+
onChange={(e) => setUserData({ ...userData, phoneNumber: e.target.value })}
134+
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
135+
/>
136+
</div>
137+
<div>
138+
<label className="block text-gray-700">Bio</label>
139+
<textarea
140+
name="bio"
141+
value={userData.bio || ''}
142+
onChange={(e) => setUserData({ ...userData, bio: e.target.value })}
143+
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
144+
/>
145+
</div>
146+
<button
147+
onClick={() => {
148+
// Handle profile save here
149+
setIsEditing(false);
150+
}}
151+
className="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600"
152+
>
153+
Save Changes
154+
</button>
155+
</form>
156+
</div>
157+
)}
158+
</div>
159+
);
160+
};
161+
162+
export default ProfilePage;

frontend/src/assets/person.jpg

136 KB
Loading

0 commit comments

Comments
 (0)