diff --git a/frontend/index.html b/frontend/index.html index 049321b84..6ad02d922 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,10 +1,18 @@ - + - - + + ArgentBank diff --git a/frontend/src/assets/css/main.css b/frontend/src/assets/css/main.css index 7c08d53aa..62ed02509 100644 --- a/frontend/src/assets/css/main.css +++ b/frontend/src/assets/css/main.css @@ -205,6 +205,19 @@ body { font-size: 1.2rem; } +.edit-input { + display: flex; + justify-content: center; + gap: 1rem; + font-size: 1.2rem; + margin-bottom: 1rem; +} + +.edit-input input { + padding: 5px; + font-size: 1.2rem; +} + .footer { display: flex; justify-content: center; @@ -260,6 +273,13 @@ body { color: #fff; font-weight: bold; padding: 10px; + width: 110px; +} + +.edit-button-wrapper { + display: flex; + justify-content: center; + gap: 1rem; } .header { diff --git a/frontend/src/layout/NavBar.jsx b/frontend/src/layout/NavBar.jsx index fdec75448..99dda62d9 100644 --- a/frontend/src/layout/NavBar.jsx +++ b/frontend/src/layout/NavBar.jsx @@ -5,8 +5,11 @@ import { authenticate } from '../service/user/userApi'; const Navbar = () => { const dispatch = useDispatch(); + // Get the isAuthenticated and user state from the store const isAuthenticated = useSelector((state) => state.auth.isAuthenticated); + const user = useSelector((state) => state.user.user); + // Handle the sign in and sign out functionality const handleAuth = () => { if (isAuthenticated) { localStorage.removeItem('token'); // Remove the token from local storage @@ -25,13 +28,23 @@ const Navbar = () => {

Argent Bank

+ + + {user && isAuthenticated ? `${user.body.firstName}` : null} + - - {isAuthenticated ? 'Sign Out' : 'Sign In'} + {isAuthenticated ? ( + <> + + Sign Out + + ) : ( + 'Sign In' + )}
diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 98366039f..a0b854272 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -15,6 +15,7 @@ const Login = () => { dispatch(authenticate({ email, password })); }; + // If the user is authenticated, redirect to the profile page useEffect(() => { if (isAuthenticated) { navigate('/profile'); diff --git a/frontend/src/pages/Profile.jsx b/frontend/src/pages/Profile.jsx index f4836ccb0..b8bfa6dd9 100644 --- a/frontend/src/pages/Profile.jsx +++ b/frontend/src/pages/Profile.jsx @@ -1,25 +1,83 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { fetchUserProfile } from '../service/user/userApi'; +import { fetchUserProfile, updateUserProfile } from '../service/user/userApi'; const Profile = () => { + const user = useSelector((state) => state.user.user); + const [firstName, setFirstName] = useState(''); + const [lastName, setLastName] = useState(''); + const [isEditing, setIsEditing] = useState(false); const dispatch = useDispatch(); - const profile = useSelector((state) => state.user); + // Fetch the user profile on component mount useEffect(() => { dispatch(fetchUserProfile()); }, [dispatch]); - console.log(profile); - return profile.user ? ( + // Set the first and last name from the user object + useEffect(() => { + if (user) { + const { firstName: userFirstName, lastName: userLastName } = user.body; + setFirstName(userFirstName); + setLastName(userLastName); + } + }, [user]); + + // Update the user profile + const handleUpdate = (e) => { + e.preventDefault(); + dispatch(updateUserProfile({ firstName, lastName })); + setIsEditing(false); + }; + + return user ? (

Welcome back
- {profile.user.body.firstName + ' ' + profile.user.body.lastName}! + {`${firstName} ${lastName}`}!

- + {isEditing ? ( +
+
+ setFirstName(e.target.value)} + /> + setLastName(e.target.value)} + /> +
+
+ + +
+
+ ) : ( + + )}

Accounts

diff --git a/frontend/src/service/store.js b/frontend/src/service/store.js index 5d101c6b4..8f981968a 100644 --- a/frontend/src/service/store.js +++ b/frontend/src/service/store.js @@ -13,6 +13,7 @@ const preloadedState = { }, }; +// The store is created with the auth and user reducers. export const store = configureStore({ reducer: { auth: authReducer, diff --git a/frontend/src/service/user/userApi.js b/frontend/src/service/user/userApi.js index be6dbdb82..4cde6f18e 100644 --- a/frontend/src/service/user/userApi.js +++ b/frontend/src/service/user/userApi.js @@ -44,3 +44,27 @@ export const fetchUserProfile = createAsyncThunk( } } ); + +// Async thunk to update user profile +export const updateUserProfile = createAsyncThunk( + 'user/updateProfile', + async ({ firstName, lastName }) => { + const token = localStorage.getItem('token'); + const response = await fetch('http://localhost:3001/api/v1/user/profile', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ firstName, lastName }), + }); + const data = await response.json(); + if (response.ok) { + console.log(data.message); + return data; + } else { + window.alert(data.message); + throw new Error(data.message); + } + } +); diff --git a/frontend/src/service/user/userSlice.js b/frontend/src/service/user/userSlice.js index d3ffb4353..8056b6d30 100644 --- a/frontend/src/service/user/userSlice.js +++ b/frontend/src/service/user/userSlice.js @@ -1,5 +1,5 @@ import { createSlice } from '@reduxjs/toolkit'; -import { authenticate, fetchUserProfile } from './userApi'; +import { authenticate, fetchUserProfile, updateUserProfile } from './userApi'; // The authSlice reducer manages the state of the user's authentication status. const authSlice = createSlice({ @@ -18,6 +18,7 @@ const authSlice = createSlice({ }, }); +// The userSlice reducer manages the state of the user's profile and loading status. const userSlice = createSlice({ name: 'user', initialState: { user: null, loading: false }, @@ -33,6 +34,15 @@ const userSlice = createSlice({ }) .addCase(fetchUserProfile.rejected, (state) => { state.loading = false; + }) + .addCase(updateUserProfile.pending, (state) => { + state.loading = true; + }) + .addCase(updateUserProfile.fulfilled, (state, action) => { + state.user = action.payload; + state.loading = false; + }).addCase(updateUserProfile.rejected, (state) => { + state.loading = false; }); }, });