Skip to content

Commit 5efbc67

Browse files
Merge branch 'main' into chat-feature-improvements
1 parent 9e7c9c2 commit 5efbc67

File tree

9 files changed

+1836
-5
lines changed

9 files changed

+1836
-5
lines changed

frontend/src/App.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ import Pomodoro from "./Components/Podomoro.jsx";
2121
import StudentView from "./Components/StudentView.jsx";
2222
import Chat from "./Components/Chat.jsx";
2323
import Booking from "./Components/Booking.jsx";
24+
import ProfilePage from "./Components/ProfilePage.jsx";
2425
import VideoCall from "./Components/VideoCall.jsx";
2526
import Calendar from "./Components/Calendar.jsx";
2627
import Inbox from "./Components/Inbox.jsx";
2728
import TutorView from "./Components/TutorView.jsx";
2829
import AssignmentPage from "./Components/AssignmentPage.jsx";
2930
import WhiteboardCanvas from "./Components/WhiteboardCanvas.jsx";
3031
import LandingPage from "./Components/LandingPage.jsx";
32+
import EditProfile from "./Components/EditProfile.jsx";
3133
import Settings from "./Components/Settings.jsx";
3234
import Questionnaire from "./Components/Questionnaire.jsx";
3335
import AssignmentCreate from "./Components/AssignmentCreate.jsx";
@@ -66,9 +68,11 @@ function App() {
6668
<Route path="/faqs" element={<FAQS />} />
6769
<Route path="/findTutor" element={<FindTutor />} />
6870
<Route path="/chat" element={<Chat />} />
71+
<Route path="/profile" element={<ProfilePage />} />
72+
<Route path="/edit-profile" element={<EditProfile />} />
73+
<Route path="/videocall" element={<VideoCall />} />
74+
<Route path="/WhiteboardCanvas" element={<WhiteboardCanvas/>} />
6975
<Route path="/WhiteboardCanvas" element={<WhiteboardCanvas />} />
70-
71-
7276
{/* Profile Setup Routes */}
7377
<Route path="/student-questionnaire" element={<Questionnaire userRole={3} />} />
7478
<Route path="/tutor-questionnaire" element={<Questionnaire userRole={1} />} />
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
import { useState, useEffect } from 'react';
2+
import { useNavigate } from 'react-router-dom';
3+
import { FaArrowLeft, FaCamera, FaEye, FaLock, FaBell } from 'react-icons/fa';
4+
import '../Styles/EditProfile.css';
5+
import userIcon from "/assets/images/UNLV_pic.png";
6+
import { getProfileData, saveProfileData, initializeProfileData } from '../utils/profileData';
7+
8+
const EditProfile = () => {
9+
const navigate = useNavigate();
10+
11+
// Initialize profile data if not already set
12+
useEffect(() => {
13+
initializeProfileData();
14+
}, []);
15+
16+
// Get profile data from localStorage or use defaults
17+
const [userData, setUserData] = useState(() => getProfileData());
18+
const [profileImagePreview, setProfileImagePreview] = useState(userData.profileImage || userIcon);
19+
const [isSaving, setIsSaving] = useState(false);
20+
const [saveSuccess, setSaveSuccess] = useState(false);
21+
22+
// Handle profile image change
23+
const handleImageChange = (e) => {
24+
if (e.target.files && e.target.files[0]) {
25+
const file = e.target.files[0];
26+
27+
// Create preview
28+
const reader = new FileReader();
29+
reader.onloadend = () => {
30+
const result = reader.result;
31+
setProfileImagePreview(result);
32+
// Update image in userData
33+
setUserData(prev => ({
34+
...prev,
35+
profileImage: result
36+
}));
37+
};
38+
reader.readAsDataURL(file);
39+
}
40+
};
41+
42+
// Handle form input change
43+
const handleChange = (e) => {
44+
const { name, value } = e.target;
45+
setUserData(prev => ({
46+
...prev,
47+
[name]: value
48+
}));
49+
};
50+
51+
// Handle privacy toggle change
52+
const handlePrivacyChange = (setting) => {
53+
setUserData(prev => ({
54+
...prev,
55+
privacy: {
56+
...prev.privacy,
57+
[setting]: !prev.privacy[setting]
58+
}
59+
}));
60+
};
61+
62+
// Handle form submission
63+
const handleSubmit = async (e) => {
64+
e.preventDefault();
65+
setIsSaving(true);
66+
67+
try {
68+
// Save to localStorage
69+
const success = saveProfileData(userData);
70+
71+
if (!success) {
72+
throw new Error('Failed to save profile data');
73+
}
74+
75+
// Show success message
76+
setSaveSuccess(true);
77+
78+
// Clear success message after 3 seconds
79+
setTimeout(() => {
80+
setSaveSuccess(false);
81+
// Navigate back to profile
82+
navigate('/profile');
83+
}, 1500);
84+
} catch (error) {
85+
console.error('Error updating profile:', error);
86+
alert('There was an error saving your profile. Please try again.');
87+
} finally {
88+
setIsSaving(false);
89+
}
90+
};
91+
92+
// Navigate back to profile without saving
93+
const handleCancel = () => {
94+
navigate('/profile');
95+
};
96+
97+
return (
98+
<div className="edit-profile-page">
99+
<div className="edit-profile-container">
100+
<div className="edit-profile-card">
101+
<div className="edit-profile-header">
102+
<h2>Edit Profile</h2>
103+
<button
104+
className="edit-profile-back-btn"
105+
onClick={handleCancel}
106+
>
107+
<FaArrowLeft /> <span>Back to Profile</span>
108+
</button>
109+
</div>
110+
111+
{saveSuccess && (
112+
<div className="save-success-message">
113+
Profile updated successfully!
114+
</div>
115+
)}
116+
117+
<form onSubmit={handleSubmit} className="edit-profile-content">
118+
{/* Profile Image Section */}
119+
<div className="profile-image-section">
120+
<div className="profile-image-container">
121+
<img
122+
src={profileImagePreview}
123+
alt="Profile"
124+
className="profile-image"
125+
/>
126+
<label className="profile-image-upload">
127+
<FaCamera />
128+
<input
129+
type="file"
130+
accept="image/*"
131+
onChange={handleImageChange}
132+
style={{ display: 'none' }}
133+
/>
134+
</label>
135+
</div>
136+
<div className="profile-image-instructions">
137+
Click the camera icon to upload a new profile photo. Square images work best.
138+
</div>
139+
</div>
140+
141+
{/* Personal Information */}
142+
<div className="form-section">
143+
<h3>Personal Information</h3>
144+
<div className="form-row">
145+
<div className="form-group">
146+
<label htmlFor="firstName">First Name</label>
147+
<input
148+
type="text"
149+
id="firstName"
150+
name="firstName"
151+
value={userData.firstName}
152+
onChange={handleChange}
153+
required
154+
/>
155+
</div>
156+
<div className="form-group">
157+
<label htmlFor="lastName">Last Name</label>
158+
<input
159+
type="text"
160+
id="lastName"
161+
name="lastName"
162+
value={userData.lastName}
163+
onChange={handleChange}
164+
required
165+
/>
166+
</div>
167+
</div>
168+
169+
<div className="form-row">
170+
<div className="form-group">
171+
<label htmlFor="username">Username</label>
172+
<input
173+
type="text"
174+
id="username"
175+
name="username"
176+
value={userData.username}
177+
onChange={handleChange}
178+
required
179+
/>
180+
<div className="input-hint">
181+
This will be displayed on your profile
182+
</div>
183+
</div>
184+
<div className="form-group">
185+
<label htmlFor="location">Location</label>
186+
<input
187+
type="text"
188+
id="location"
189+
name="location"
190+
value={userData.location}
191+
onChange={handleChange}
192+
/>
193+
</div>
194+
</div>
195+
196+
<div className="form-row">
197+
<div className="form-group">
198+
<label htmlFor="birthdate">Date of Birth</label>
199+
<input
200+
type="date"
201+
id="birthdate"
202+
name="birthdate"
203+
value={userData.birthdate}
204+
onChange={handleChange}
205+
/>
206+
</div>
207+
</div>
208+
</div>
209+
210+
{/* Contact Information */}
211+
<div className="form-section">
212+
<h3>Contact Information</h3>
213+
<div className="form-row">
214+
<div className="form-group">
215+
<label htmlFor="email">Email Address</label>
216+
<input
217+
type="email"
218+
id="email"
219+
name="email"
220+
value={userData.email}
221+
onChange={handleChange}
222+
required
223+
/>
224+
</div>
225+
<div className="form-group">
226+
<label htmlFor="phone">Phone Number</label>
227+
<input
228+
type="tel"
229+
id="phone"
230+
name="phone"
231+
value={userData.phone}
232+
onChange={handleChange}
233+
/>
234+
</div>
235+
</div>
236+
</div>
237+
238+
{/* Bio */}
239+
<div className="form-section">
240+
<h3>Bio</h3>
241+
<div className="form-group">
242+
<label htmlFor="bio">About Me</label>
243+
<textarea
244+
id="bio"
245+
name="bio"
246+
value={userData.bio}
247+
onChange={handleChange}
248+
placeholder="Tell us a bit about yourself..."
249+
/>
250+
<div className="input-hint">
251+
Max 300 characters
252+
</div>
253+
</div>
254+
</div>
255+
256+
<div className="form-divider"></div>
257+
258+
{/* Privacy Settings */}
259+
<div className="form-section">
260+
<h3>Privacy Settings</h3>
261+
<div className="privacy-settings">
262+
<div className="privacy-option">
263+
<div className="privacy-option-header">
264+
<div className="privacy-option-icon">
265+
<FaEye />
266+
</div>
267+
<h4 className="privacy-option-title">Profile Visibility</h4>
268+
</div>
269+
<p className="privacy-option-description">
270+
Allow others to view your full profile details
271+
</p>
272+
<label className="toggle-switch">
273+
<input
274+
type="checkbox"
275+
checked={userData.privacy?.profileVisibility}
276+
onChange={() => handlePrivacyChange('profileVisibility')}
277+
/>
278+
<span className="toggle-slider"></span>
279+
</label>
280+
</div>
281+
282+
<div className="privacy-option">
283+
<div className="privacy-option-header">
284+
<div className="privacy-option-icon">
285+
<FaLock />
286+
</div>
287+
<h4 className="privacy-option-title">Activity Visibility</h4>
288+
</div>
289+
<p className="privacy-option-description">
290+
Show your learning activities to other users
291+
</p>
292+
<label className="toggle-switch">
293+
<input
294+
type="checkbox"
295+
checked={userData.privacy?.activityVisibility}
296+
onChange={() => handlePrivacyChange('activityVisibility')}
297+
/>
298+
<span className="toggle-slider"></span>
299+
</label>
300+
</div>
301+
302+
<div className="privacy-option">
303+
<div className="privacy-option-header">
304+
<div className="privacy-option-icon">
305+
<FaBell />
306+
</div>
307+
<h4 className="privacy-option-title">Email Notifications</h4>
308+
</div>
309+
<p className="privacy-option-description">
310+
Receive updates about courses and messages
311+
</p>
312+
<label className="toggle-switch">
313+
<input
314+
type="checkbox"
315+
checked={userData.privacy?.emailNotifications}
316+
onChange={() => handlePrivacyChange('emailNotifications')}
317+
/>
318+
<span className="toggle-slider"></span>
319+
</label>
320+
</div>
321+
</div>
322+
</div>
323+
324+
{/* Action Buttons */}
325+
<div className="button-group">
326+
<button
327+
type="button"
328+
className="btn-cancel"
329+
onClick={handleCancel}
330+
>
331+
Cancel
332+
</button>
333+
<button
334+
type="submit"
335+
className="btn-save"
336+
disabled={isSaving}
337+
>
338+
{isSaving ? 'Saving...' : 'Save Changes'}
339+
</button>
340+
</div>
341+
</form>
342+
</div>
343+
</div>
344+
</div>
345+
);
346+
};
347+
348+
export default EditProfile;

0 commit comments

Comments
 (0)