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

[CHE-7] Create EditProfile Page #55

2 changes: 2 additions & 0 deletions client/src/AuthenticatedApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import MainPage from "./pages/MainPage/MainPage";
import Forums from "./pages/Forums/Forums";
import Profiles from "./pages/Profiles/Profiles";
import Profile from "./pages/Profile/Profile";
import EditProfilePage from "./pages/EditProfilePage/EditProfilePage";
import NotFoundPage from "./pages/NotFoundPage/NotFoundPage";
import { useNavigate } from "react-router-dom";

Expand All @@ -27,6 +28,7 @@ const AuthenticatedApp = () => {
<Route path="main" element={<MainPage />} />
<Route path="/profiles" element={<Profiles />} />
<Route path="/profile" element={<Profile />} />
<Route path="/editProfile" element={<EditProfilePage />} />
<Route path="/forums" element={<Forums />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/Banner/Banner.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe("Banner Component", () => {
const optionsButton = screen.getByRole("button", { name: "Options" });
fireEvent.click(optionsButton);

const profileOption = screen.getByText("Go to Profile");
const profileOption = screen.getByText("Edit Profile");
const logoutOption = screen.getByText("Logout");

expect(profileOption).toBeInTheDocument();
Expand All @@ -74,10 +74,10 @@ describe("Banner Component", () => {
const optionsButton = screen.getByRole("button", { name: "Options" });
fireEvent.click(optionsButton);

const profileOption = screen.getByText("Go to Profile");
const profileOption = screen.getByText("Edit Profile");
fireEvent.click(profileOption);

expect(mockNavigate).toHaveBeenCalledWith("profile");
expect(mockNavigate).toHaveBeenCalledWith("editProfile");
});

it("handles logout on clicking Logout", () => {
Expand Down
8 changes: 4 additions & 4 deletions client/src/components/Banner/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const Banner = (): JSX.Element => {
//TODO CLEAR ALL STATE
};

const goToProfile = () => {
navigate("profile");
const goToEditProfile = () => {
navigate("editProfile");
setShowDropdown(false);
};
return (
Expand All @@ -40,9 +40,9 @@ const Banner = (): JSX.Element => {
<a
href="#!"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={goToProfile}
onClick={goToEditProfile}
>
Go to Profile
Edit Profile
</a>
<a
href="#!"
Expand Down
32 changes: 31 additions & 1 deletion client/src/features/userProfile/userProfileSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IProfile } from "../../../types/profile";

export interface ProfileState {
profile: IProfile | null;
status: "idle" | "loading" | "failed";
status: "idle" | "loading" | "failed" | "updating";
error: string | null;
}

Expand All @@ -31,6 +31,25 @@ export const fetchUserProfile = createAsyncThunk(
}
);

export const updateUserProfile = createAsyncThunk(
"profile/updateUserProfile",
async (
{ userID, ...updateData }: Partial<IProfile> & { userID: string },
thunkAPI
) => {
try {
const response = await axios.put(`/api/profiles/${userID}`, updateData);
return response.data;
} catch (error) {
let errorMessage = "An error occurred during profile update";
if (axios.isAxiosError(error)) {
errorMessage = error.response?.data || errorMessage;
}
return thunkAPI.rejectWithValue(errorMessage);
}
}
);

const userProfileSlice = createSlice({
name: "profile",
initialState,
Expand All @@ -53,6 +72,17 @@ const userProfileSlice = createSlice({
.addCase(fetchUserProfile.rejected, (state, action) => {
state.status = "failed";
state.error = action.payload as string;
})
.addCase(updateUserProfile.pending, (state) => {
state.status = "updating";
})
.addCase(updateUserProfile.fulfilled, (state, action) => {
state.profile = action.payload; // Assuming the backend returns the updated profile
state.status = "idle";
})
.addCase(updateUserProfile.rejected, (state, action) => {
state.status = "failed";
state.error = action.payload as string;
});
},
});
Expand Down
95 changes: 95 additions & 0 deletions client/src/pages/EditProfilePage/EditProfilePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useEffect, useState, ChangeEvent, FormEvent } from "react";
import { useAppSelector, useAppDispatch } from "../../app/hooks";
import {
fetchUserProfile,
updateUserProfile,
} from "../../features/userProfile/userProfileSlice";

const EditProfilePage = () => {
const dispatch = useAppDispatch();
const { profile, status } = useAppSelector((state) => state.userProfile);
const userID = useAppSelector((state) => state.user.userData?._id);

const [formData, setFormData] = useState({
fullName: "",
email: "",
personalBio: "",
});

useEffect(() => {
if (userID) dispatch(fetchUserProfile(userID as string));
}, [dispatch]);

useEffect(() => {
if (profile) {
setFormData({
fullName: profile.fullName || "",
email: profile.email || "",
personalBio: profile.personalBio || "",
});
}
}, [profile]);

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prevFormData) => ({
...prevFormData,
[name]: value,
}));
};

const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!userID) {
console.error("UserID is undefined.");
return;
}
dispatch(updateUserProfile({ ...formData, userID }));
};

if (status === "loading" || !userID) {
return <div>Loading...</div>;
}

return (
<div>
<h2>Edit Profile</h2>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="fullName">Full Name:</label>
<input
type="text"
id="fullName"
name="fullName"
value={formData.fullName}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="email">Email:</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="personalBio">Personal Bio:</label>
<input
type="text"
id="personalBio"
name="personalBio"
value={formData.personalBio}
onChange={handleChange}
/>
</div>

<button type="submit">Save Changes</button>
</form>
</div>
);
};

export default EditProfilePage;
5 changes: 3 additions & 2 deletions client/src/pages/Profiles/Profiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ const Profiles = (): JSX.Element => {
<h1 className="text-4xl font-extrabold mb-4">PROFILES</h1>
</div>
<div>
{profiles.map((profile) => (
<ProfileThumb key={profile._id} profile={profile} />
{/* TODO Look at better key for this */}
{profiles.map((profile, index) => (
<ProfileThumb key={index} profile={profile} />
))}
</div>
</>
Expand Down
12 changes: 5 additions & 7 deletions server/controllers/profileController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const createProfile = async (
}
};

// ENDPOINT PATCH api/profiles/:UserID
// ENDPOINT PUT api/profiles/:UserID
// PURPOSE Update an existing profile
// ACCESS Private
const updateProfile = async (
Expand All @@ -87,13 +87,11 @@ const updateProfile = async (
next: NextFunction
) => {
const { userID } = req.params;
const { firstName, lastName, bio, job, socials } = req.body;
const { fullName, email, personalBio } = req.body;
const newProfile = {
firstName,
lastName,
bio,
job,
socials,
fullName,
email,
personalBio,
};

try {
Expand Down
1 change: 0 additions & 1 deletion server/models/profileModel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// profileModel.ts
import mongoose, { Schema } from "mongoose";
import { IProfile } from "../types/profile";

Expand Down
2 changes: 1 addition & 1 deletion server/routes/profileRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
const router = express.Router();

router.post("/", createProfile);
router.patch("/:userID", updateProfile);
router.put("/:userID", updateProfile);
router.get("/:userID", getProfileById);
router.get("/", getAllProfiles);

Expand Down
Loading