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

Update the user information from the Profile Page #878

Merged
merged 5 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions backend/controllers/authController.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,5 +254,30 @@ exports.resendVerificationEmail = async (req, res) => {
};


exports.updateUserProfile = async (req, res) => {
const {userId} = req.query; // Retrieved from authentication middleware
const { firstName, lastName, username, email, address } = req.body;

// Validate the fields
if (firstName.length < 2 || lastName.length < 2) {
return res.status(400).json({ message: 'First and Last names must be at least 2 characters long.' });
}

try {
const user = await User.findById(userId);
if (!user) return res.status(404).json({ message: 'User not found' });

// Update the fields only if provided
if (firstName) user.firstName = firstName;
if (lastName) user.lastName = lastName;
if (username) user.username = username;
if (email) user.email = email;
if (address) user.address = address;

await user.save();
res.status(200).json({ message: 'Profile updated successfully', user });
} catch (error) {
res.status(500).json({ message: 'Server error while updating profile', error });
}
};

3 changes: 2 additions & 1 deletion backend/controllers/userController.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ exports.getUserByID =async (req, res) => {
} catch (error) {
console.error('Error deleting unverified accounts:', error);
}
};
};

9 changes: 5 additions & 4 deletions backend/model/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
username: { type: String, required: true, unique: true, trim: true,minlength: 3, maxlength: 30 },
email: { type: String, required: true, unique: true },
firstName: { type: String, required: true, minlength: 2 },
lastName: { type: String, required: true, minlength: 2 },
username: { type: String, required: true, unique: true, trim: true, minlength: 3, maxlength: 30 },
email: { type: String, required: true, unique: true, match: /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/ },
address: { type: String, maxlength: 100 },
password: { type: String },
role: {
type: String,
Expand Down
4 changes: 3 additions & 1 deletion backend/routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ const {
checkUsernameAvailability,
verifyEmail,
checkEmailAvailability,
resendVerificationEmail
resendVerificationEmail,
updateUserProfile
} = require("../controllers/authController");

router.post("/signup", signupController);
router.put("/profile-update", updateUserProfile);
router.get("/check-username/:username", checkUsernameAvailability);
router.get("/check-email/:email", checkEmailAvailability);
router.get("/verify-account", verifyEmail);
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/AgroRentAI/RentUserDashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import React, { useState, useEffect } from "react";

import {
Expand Down Expand Up @@ -29,6 +30,7 @@ const RentUserDashboard = () => {
email: "",
address: "",
profilePicture: "",

paymentMethods: [],
});
const [rentals, setRentals] = useState([]);
Expand All @@ -51,11 +53,13 @@ const RentUserDashboard = () => {

// Mock API calls
const fetchUserProfile = async () => ({

name: "John Doe",
email: "[email protected]",
address: "123 Main St, Springfield, USA",
profilePicture: "/images/profile.png",
paymentMethods: ["Visa **** 1234", "Mastercard **** 5678"],

});

const fetchUserRentals = async () => [
Expand Down Expand Up @@ -98,14 +102,12 @@ const RentUserDashboard = () => {
setRentals(rentals.filter((rental) => rental.id !== id));
};

const handleProfileEdit = () => {
alert("Profile edit functionality to be implemented.");
};

const renderSectionContent = () => {
switch (activeSection) {
case "Account Information":

return <ProfileComponent profile={profile} />;

case "Rental History":
return <RentalHistoryComponent rentals={rentals} />;
case "Payment Methods & Billing":
Expand Down
112 changes: 89 additions & 23 deletions frontend/src/AgroRentAI/components/AccountInformation.jsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,99 @@
// ProfileComponent.js
import React, { useState } from 'react';
import AvatarComponent from './Avatar'; // Import the AvatarComponent
import React, { useState, useEffect } from 'react';
import AvatarComponent from './Avatar';
import { toast, ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const ProfileComponent = ({ profile, handleProfileEdit, updateProfile }) => {
const ProfileComponent = ({ profile, setProfile }) => {
const [isEditing, setIsEditing] = useState(false);
const [formData, setFormData] = useState({
firstName: profile.firstName || 'user',
lastName: profile.lastName || 'user',
username: profile.username || 'username',
email: profile.email || '[email protected]',
address: profile.address || 'user street address'
firstName: profile.firstName || '',
lastName: profile.lastName || '',
username: profile.username || '',
email: profile.email || '',
address: profile.address || '',
});
const [errors, setErrors] = useState({});

const ApiUrl = process.env.NODE_ENV === 'production'
? 'https://agrotech-ai-11j3.onrender.com'
: 'http://localhost:8080';

// Update formData when profile prop changes
useEffect(() => {
setFormData({
firstName: profile.firstName || '',
lastName: profile.lastName || '',
username: profile.username || '',
email: profile.email || '',
address: profile.address || '',
});
}, [profile]);


const validateInput = () => {
let validationErrors = {};
const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;

if (formData.firstName.length < 2) validationErrors.firstName = "First name must be at least 2 characters.";
if (formData.lastName.length < 2) validationErrors.lastName = "Last name must be at least 2 characters.";
if (formData.username.length < 3 || formData.username.length > 30) validationErrors.username = "Username must be between 3 and 30 characters.";
if (!emailRegex.test(formData.email)) validationErrors.email = "Invalid email format.";

setErrors(validationErrors);
return Object.keys(validationErrors).length === 0;
};

const handleInputChange = (e) => {
const { name, value } = e.target;
setFormData((prevData) => ({
...prevData,
[name]: value
[name]: value,
}));
};

const handleSubmit = (e) => {
const handleToggleEdit = () => {
setIsEditing(!isEditing);
if (!isEditing) setFormData({...profile }); // Reset form data to original profile when editing is cancelled
};
const handleSubmit = async (e) => {
e.preventDefault();
updateProfile(formData);
setIsEditing(false); // Stop editing once the form is submitted

if (!validateInput()) {
toast.error('Please fix validation errors.');
return;
}

try {
const response = await fetch(`${ApiUrl}/auth/profile-update?userId=${profile.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});

if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Failed to update profile');
}

const updatedUser = await response.json();
setProfile(updatedUser); // Update the profile in parent component
toast.success('Profile updated successfully!');
} catch (error) {
console.error('Error updating profile:', error);
toast.error(error.message || 'Error updating profile. Please try again.');
}
};

return (
<div>
<ToastContainer />

{!isEditing ? (
<div>
{/* Displaying the Avatar */}
<div className="flex items-center mb-4">
<AvatarComponent
profilePicture={profile.profilePicture}
name={profile.firstName} // Ensure profile.firstName exists and is valid
/>
<AvatarComponent profilePicture={profile.profilePicture} name={profile.firstName} />
<div className="ml-4">
<p className="text-green-700"><strong>Name:</strong> {profile.firstName} {profile.lastName}</p>
<p className="text-green-700"><strong>Username:</strong> {profile.username}</p>
Expand All @@ -50,7 +108,6 @@ const ProfileComponent = ({ profile, handleProfileEdit, updateProfile }) => {
</button>
</div>
) : (
// Edit Profile Form
<form onSubmit={handleSubmit} className="mt-6">
<div className="flex flex-col mb-4">
<label className="text-green-600">First Name</label>
Expand All @@ -59,39 +116,47 @@ const ProfileComponent = ({ profile, handleProfileEdit, updateProfile }) => {
name="firstName"
value={formData.firstName}
onChange={handleInputChange}
className="px-4 py-2 border rounded-md"
className={`px-4 py-2 border rounded-md ${errors.firstName ? 'border-red-500' : ''}`}
/>
{errors.firstName && <p className="text-red-500 text-sm">{errors.firstName}</p>}
</div>

<div className="flex flex-col mb-4">
<label className="text-green-600">Last Name</label>
<input
type="text"
name="lastName"
value={formData.lastName}
onChange={handleInputChange}
className="px-4 py-2 border rounded-md"
className={`px-4 py-2 border rounded-md ${errors.lastName ? 'border-red-500' : ''}`}
/>
{errors.lastName && <p className="text-red-500 text-sm">{errors.lastName}</p>}
</div>

<div className="flex flex-col mb-4">
<label className="text-green-600">Username</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleInputChange}
className="px-4 py-2 border rounded-md"
className={`px-4 py-2 border rounded-md ${errors.username ? 'border-red-500' : ''}`}
/>
{errors.username && <p className="text-red-500 text-sm">{errors.username}</p>}
</div>

<div className="flex flex-col mb-4">
<label className="text-green-600">Email</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleInputChange}
className="px-4 py-2 border rounded-md"
className={`px-4 py-2 border rounded-md ${errors.email ? 'border-red-500' : ''}`}
/>
{errors.email && <p className="text-red-500 text-sm">{errors.email}</p>}
</div>

<div className="flex flex-col mb-4">
<label className="text-green-600">Address</label>
<textarea
Expand All @@ -101,6 +166,7 @@ const ProfileComponent = ({ profile, handleProfileEdit, updateProfile }) => {
className="px-4 py-2 border rounded-md"
/>
</div>

<div className="flex justify-between">
<button
type="submit"
Expand Down
Loading