Skip to content

add task solution#300

Open
IvanovvvIhor wants to merge 2 commits into
mate-academy:masterfrom
IvanovvvIhor:develop
Open

add task solution#300
IvanovvvIhor wants to merge 2 commits into
mate-academy:masterfrom
IvanovvvIhor:develop

Conversation

@IvanovvvIhor

Copy link
Copy Markdown

No description provided.

@mateacademy-ai-mentor mateacademy-ai-mentor left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

I am requesting changes because there are still a few critical blockers against the checklist that break required functionality. Most importantly, your backend authMiddleware validates the access token but never assigns the decoded user to req.user, yet updateProfile relies on req.user.id and req.user.email; this means all profile updates (name, password, email) will fail at runtime and violates [CHECKLIST ITEM #19#21]. On the frontend, after successful activation you redirect to /?activated=true instead of /profile, and after successful login you don’t navigate to /profile at all, which both conflict with the requirements to redirect to the profile page after activation and login ([CHECKLIST ITEM #7] and [CHECKLIST ITEM #10]). Your login UI also never displays state.auth.error, so inactive users don’t see the backend message asking them to activate their email, which is required by [CHECKLIST ITEM #9].

The critical issues you must fix are:

  • In user.middleware.js (and any equivalent middleware files), after validating the access token, set req.user = userData; before next() so updateProfile can access the current user.
  • In ActivationPage, change the post-success navigation target from navigate('/?activated=true') to navigate('/profile') (keeping or removing the delay as you prefer).
  • In LoginModal, either await loginUser(...).unwrap() and then call navigate('/profile'), or observe isAuth via useSelector and redirect to /profile when it becomes true.
  • Also in LoginModal, select state.auth.error from Redux and render it in the modal so backend errors, especially “Акаунт не активовано. Перевірте пошту”, are visible to users.

Minor but recommended improvements (not blockers) include adding route guards that redirect authenticated users away from public auth pages (activation, forgot/reset, home) to strictly enforce “only non authenticated”, and using the updateProfile thunk in ProfilePage to keep Redux user data in sync and show success/error messages. Overall you’ve implemented the majority of the flows correctly; once you address the middleware bug and the activation/login redirect and error-handling issues, your solution will be in good shape to meet the full specification.


✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨

Important

Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.

Tip

If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.

Caution

Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.

Footnotes

  1. Rate AI review example

  2. Re-request AI review example

  3. Add AI review comments example

Comment thread frontend/src/App.css
Comment on lines +27 to +29
box-sizing: border-box;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirements state that if a user is not active, you should ask them to activate their email (checklist item #9). Currently, you dispatch loginUser but never read state.auth.error or show any feedback, so an inactive-account error from the backend will be invisible. Consider selecting the error from the store and rendering it in the modal so users know they must activate via the email link.

Comment thread frontend/src/App.css
Comment on lines +32 to +67
color: var(--text-primary);
font-family: 'Inter', system-ui, sans-serif;
line-height: 1.6;
transition: background-color 0.3s, color 0.3s;
}

/* Фікс накладання: додаємо gap для контейнерів */
.page-container {
min-height: 100vh;
padding: 2rem;
display: flex;
flex-direction: column;
gap: 1.5rem; /* Запобігає прилипанню елементів */
}

.centered {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}

.auth-card {
background: var(--card-bg);
padding: 2.5rem;
border-radius: 12px;
border: 1px solid var(--border);
width: 90%;
max-width: 400px;
display: flex;
flex-direction: column;
gap: 1rem; /* Відступи всередині картки */
}

/* Навігація: додаємо flex-wrap, щоб меню не наїжджало на лого при вузькому екрані */
.navbar {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checklist item #10 requires redirecting to the profile page after a successful login. This component does not navigate anywhere on successful authentication, so users remain on the home page. You could observe isAuth from the Redux state with useSelector and call navigate('/profile') when it becomes true, or handle this in a higher-level route component.

Comment on lines +27 to +29
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
dispatch(loginUser({ email, password }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to checklist item #10, after a successful login the user should be redirected to the profile page, but this handler only dispatches loginUser without navigating. Consider awaiting the thunk and, on success, calling navigate('/profile') here.

Comment on lines +38 to +67
<form className="auth-form" onSubmit={handleSubmit}>
<div className="form-group">
<label>Електронна пошта</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="email@example.com"
required
/>
</div>

<div className="form-group">
<label>Пароль</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
required
/>
</div>

<div className="form-helpers">
<button type="button" className="link-btn" onClick={handleForgot}>
Забули пароль?
</button>
</div>

<button type="submit" className="btn-primary full-width">Увійти</button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirements state that inactive users should be prompted to activate their email on login (checklist item #9). The backend returns a specific error for non-active accounts, but this modal doesn’t read or display any error message from the auth slice, so the user never sees that hint.

Comment on lines +31 to +36
try {
await dispatch(registerUser({ name, email, password })).unwrap();
setIsSuccess(true);
} catch (error) {
console.error(error);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of registration failure (e.g., email already in use or backend password validation), you catch the error and only log it to the console. While not explicitly required by the checklist, surfacing this error to the user (e.g., via local state or authSlice.error) would make the registration flow clearer and help users fix issues.

Comment on lines +23 to +42
{!isAuth ? (
<div className="navbar-menu">
<button className="btn-secondary" onClick={() => dispatch(openRegisterModal())}>
Зареєструватися
</button>
<button className="btn-primary" onClick={() => dispatch(openLoginModal())}>
Увійти
</button>
</div>
) : (
<div className="navbar-menu">
<span className="user-name">Вітаю, {user?.name || 'Користувач'}</span>
<button className="btn-secondary" onClick={() => navigate('/profile')}>
Профіль
</button>
<button className="btn-danger" onClick={handleLogout}>
Вийти
</button>
</div>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your Navbar hides the register/login buttons when isAuth is true, which partially enforces "only non authenticated" for those actions. However, users can still directly access other non-auth-only routes like forgot/reset/activation via URL. To strictly respect checklist items #1, #5, #8, and #13, consider adding route guards or redirects on those pages when isAuth is true.

Comment on lines +10 to +17
const navigate = useNavigate();

const handleLogout = () => {
dispatch(logoutLocal());
navigate('/');
};

return (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ProtectedRoute correctly prevents unauthenticated users from accessing the profile page by redirecting to /, satisfying the "Profile page only for authenticated" requirement (checklist item #18). Note that similar guarding is not applied to non-auth-only pages, which is why authenticated users can still visit forgot/reset/activation routes.

Comment on lines +10 to +14
const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
const { isAuth } = useSelector((state: RootState) => state.auth);

if (!isAuth) {
return <Navigate to="/" replace />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ProtectedRoute to guard /profile ensures the profile page is available only for authenticated users, satisfying checklist item #18. However, there is no corresponding guard that prevents authenticated users from opening the login/register flows; if you need strict “only non authenticated” enforcement (items #1 and #8), consider redirecting authenticated users away from the public auth UI.

Comment on lines +12 to +14

if (!isAuth) {
return <Navigate to="/" replace />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The navbar correctly hides login/register buttons when isAuth is true and shows profile/logout instead, and handleLogout returns the user to the home route. This effectively meets the logout redirect requirement (#12), but if the spec expects a dedicated /login page, you might want to navigate to such a route instead of /.

Comment on lines +34 to +35
setTimeout(() => {
navigate('/?activated=true');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirements state that after activation you should redirect to the Profile page (checklist item #7), but here you navigate to the home route with a query ('/?activated=true'). To align with the spec, this redirect should go to /profile once activation succeeds.

Comment on lines +6 to +15

const HomePage = () => {
const { isLoginOpen, isRegisterOpen } = useSelector(
(state: RootState) => state.auth
);

return (
<div className="page-container">
<Navbar />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This page is used as the 404 route and returns a clear "Сторінку не знайдено" message with a button to go home, which fulfills checklist item #22 about having a 404 page for all other routes.

Comment on lines +13 to +33
const handleNameChange = async (e: React.FormEvent) => {
e.preventDefault();
await authService.updateProfile({ name });
};

const handlePasswordChange = async (e: React.FormEvent) => {
e.preventDefault();
if (passwords.new !== passwords.confirm) return alert('Паролі не збігаються');
await authService.updateProfile({
oldPassword: passwords.old,
newPassword: passwords.new,
confirmation: passwords.confirm
});
};

const handleEmailChange = async (e: React.FormEvent) => {
e.preventDefault();
await authService.updateProfile({
newEmail: emailData.newEmail,
password: emailData.confirmPassword
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The profile handlers call authService.updateProfile directly but don’t handle any response or errors in the UI. Even though the checklist doesn’t mandate success/error messages, using the existing updateProfile thunk from authSlice here would keep Redux state (e.g., updated name/email) in sync and allow you to surface backend errors to the user.

Comment on lines +24 to +49
confirmation: passwords.confirm
});
};

const handleEmailChange = async (e: React.FormEvent) => {
e.preventDefault();
await authService.updateProfile({
newEmail: emailData.newEmail,
password: emailData.confirmPassword
});
};

return (
<div className="page-container profile-layout">
<aside className="profile-sidebar">
<div className="profile-avatar">{user?.name?.charAt(0) || 'U'}</div>
<h3>{user?.name || 'Користувач'}</h3>
<p>{user?.email || 'email@example.com'}</p>
{user?.isActive && <span className="badge-active">Акаунт активовано</span>}
</aside>

<main className="profile-content">
<h2>Налаштування профілю</h2>

<section className="profile-section">
<h3>Змінити ім'я</h3>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reset password form correctly enforces that password and confirmation must be equal before submitting, satisfying checklist item #16, and shows a success page with a link to login (item #17). Ensure that the login route or modal is easily reachable from / so the "Перейти до входу" link leads users to an actual login UI.

Comment on lines +13 to +33
const ResetPassword = () => {
const { token } = useParams<{ token: string }>();

const [password, setPassword] = useState('');
const [confirmation, setConfirmation] = useState('');
const [isSuccess, setIsSuccess] = useState(false);
const [error, setError] = useState('');

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');

if (password !== confirmation) {
return setError('Паролі не збігаються!');
}

try {
if (token) {
await authService.resetPassword(token, password, confirmation);
setIsSuccess(true);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This page correctly meets the profile requirements by allowing name change, password change with old/new/confirmation, and email change with current password and new email data, while the backend sends notification to the old email. For a better UX, consider handling and displaying success/failure messages from authService.updateProfile rather than just awaiting the calls.

Comment on lines +24 to +49

if (password !== confirmation) {
return setError('Паролі не збігаються!');
}

try {
if (token) {
await authService.resetPassword(token, password, confirmation);
setIsSuccess(true);
}
} catch (err) {
const errorData = err as AxiosErrorResponse;
setError(errorData.response?.data?.error || 'Токен застарів або невалідний');
}
};

if (isSuccess) {
return (
<div className="page-container centered">
<div className="auth-card text-center">
<h2>Пароль змінено!</h2>
<p>Тепер ви можете увійти в акаунт з новими даними.</p>
<Link to="/" className="btn-primary full-width">Перейти до входу</Link>
</div>
</div>
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reset password flow enforces matching password and confirmation before submitting, and shows a success page with a link back to /, which in your app serves as the login entry point via the home/login modal. This aligns with checklist items #16 and #17.

Comment on lines +28 to +32
});

api.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a logout method on authService, but the slice never uses it; all logout behaviour is purely local (logoutLocal). If your backend needs to clear refresh tokens or session data, you may want to add a logoutUser thunk that calls authService.logout and then dispatches logoutLocal to keep server and client in sync.

Comment on lines +39 to +47
forgotPassword: async (email: string): Promise<AuthResponse> => {
const { data } = await api.post('/forgot-password', { email });
return data;
},

activation: async (email: string, token: string): Promise<AuthResponse> => {
const { data } = await api.get(`/auth/activation/${email}/${token}`);
return data;
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refresh interceptor correctly dispatches auth/setCredentials on success and auth/logoutLocal on failure, but the app’s logout flow in Navbar currently uses only logoutLocal and doesn’t call the /logout endpoint. While the checklist doesn’t explicitly demand server-side logout, using authService.logout() in addition to logoutLocal would ensure refresh tokens are cleared on the backend too.

Comment on lines +68 to +147
async (credentials, { rejectWithValue }) => {
try {
return await authService.login(credentials.email, credentials.password);
} catch (err: unknown) {
const error = err as AxiosErrorResponse;
return rejectWithValue(error.response?.data?.error || 'Помилка входу');
}
}
);

export const updateProfile = createAsyncThunk<UpdateProfileData, UpdateProfileData, { rejectValue: string }>(
'auth/updateProfile',
async (profileData, { rejectWithValue }) => {
try {
await authService.updateProfile(profileData);
return profileData;
} catch (err: unknown) {
const error = err as AxiosErrorResponse;
return rejectWithValue(error.response?.data?.error || 'Помилка оновлення');
}
}
);

const initialState: AuthState = {
user: null,
accessToken: null,
isAuth: false,
isLoginOpen: false,
isRegisterOpen: false,
isLoading: false,
error: null,
};

const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
openLoginModal: (state) => {
state.isLoginOpen = true;
state.isRegisterOpen = false;
state.error = null;
},
closeLoginModal: (state) => {
state.isLoginOpen = false;
},
openRegisterModal: (state) => {
state.isRegisterOpen = true;
state.isLoginOpen = false;
state.error = null;
},
closeRegisterModal: (state) => {
state.isRegisterOpen = false;
},
setCredentials: (state, action: PayloadAction<AuthResponse>) => {
state.user = action.payload.user;
state.accessToken = action.payload.accessToken;
state.isAuth = true;
},
logoutLocal: (state) => {
state.user = null;
state.accessToken = null;
state.isAuth = false;
}
},
extraReducers: (builder) => {
builder
.addCase(loginUser.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(loginUser.fulfilled, (state, action: PayloadAction<AuthResponse>) => {
state.isLoading = false;
state.isAuth = true;
state.user = action.payload.user;
state.accessToken = action.payload.accessToken;
state.isLoginOpen = false;
})
.addCase(loginUser.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload ? String(action.payload) : 'Помилка входу';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loginUser correctly propagates backend error messages (including the inactive-account case) into state.error. However, your login UI currently doesn’t read or display this error, so users never see that they must activate their email (checklist item #9). Ensure the login modal selects and renders state.auth.error.

Comment on lines +149 to +161
.addCase(registerUser.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(registerUser.fulfilled, (state) => {
state.isLoading = false;
state.isRegisterOpen = false;
state.isLoginOpen = true;
})
.addCase(registerUser.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload ? String(action.payload) : 'Помилка реєстрації';
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On registerUser.fulfilled you close the register modal and open the login modal, which is a good flow. For full alignment with the requirements, make sure the activation page and subsequent login redirect to the profile page (item #7 and #10), which will rely on setCredentials and isAuth managed here.

Comment on lines +13 to +21
const userData = jwt.validateAccessToken(accessToken);

if (!userData) {
res.status(401).json({ message: 'Invalid token' });

return;
}

next();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You validate the access token but never attach the decoded user information to req. Since updateProfile in the controller expects req.user.id and req.user.email, req.user will be undefined, breaking profile updates (checklist items #19#21). After successful validation, assign req.user = userData before calling next().

Comment on lines +13 to +21
return res
.status(400)
.json({ error: "Усі поля (name, email, password) є обов'язковими" });
}

const newUser = await UserService.register(name, email, password);

return res.status(201).json(newUser);
} catch (error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authMiddleware validates the access token but never attaches the decoded user data to req.user, yet userController.updateProfile expects req.user.id and req.user.email. This will make all profile updates fail and violates the profile-related requirements (checklist items #19#21). You should set req.user = userData before calling next().

Comment on lines +26 to +51
activation: async (req, res) => {
try {
const { email, token } = req.params;

console.log('--- RAW ACTIVATION ---');
console.log('Email in URL:', email);
console.log('Token in URL:', token);

const user = await UserService.findByEmail(email);

if (!user) {
return res.status(404).json({ error: 'Користувача не знайдено' });
}

console.log('Token in DB:', user.activationToken);
console.log('Are tokens equal?', user.activationToken === token);
console.log('Length in DB:', user.activationToken?.length);
console.log('Length in URL:', token?.length);

if (user.activationToken !== token) {
return res.status(404).json({ error: 'Невалідний токен' });
}

await UserService.activation(email, token);

return res.status(200).json({ message: 'Акаунт успішно активовано' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The activation endpoint correctly validates the token and activates the account, but the frontend activation flow currently redirects the user back to /?activated=true instead of the Profile page. Checklist item #7 explicitly requires redirecting to Profile after activation, so you’ll need to adjust the frontend redirect target.

Comment on lines +59 to +92
login: async (req, res) => {
try {
const { email, password } = req.body;

if (!email || !password) {
return res.status(400).json({ error: 'Введіть всі поля' });
}

const user = await UserService.login(email, password);

if (!user) {
return res.status(404).json({ error: 'Помилка під час авторизації' });
}

const normalizedUser = UserService.normalize(user);
const refreshToken = jwt.generateRefreshToken(normalizedUser);

await UserService.saveToken(user.id, refreshToken);

res.cookie('refreshToken', refreshToken, {
maxAge: 30 * 24 * 60 * 60 * 1000,
httpOnly: true,
sameSite: 'none',
secure: true,
});

return res.status(200).json({
user: normalizedUser,
accessToken: jwt.generateAccessToken(normalizedUser),
});
} catch (error) {
return res
.status(401)
.json({ error: error.message || 'Помилка під час авторизації' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The login controller and service correctly enforce that inactive users cannot log in and return a specific error message. However, the frontend login modal does not render state.auth.error, so users aren’t explicitly asked to activate their email, which is required by checklist item #9. Make sure the UI shows these backend error messages.

Comment on lines +59 to +88
login: async (req, res) => {
try {
const { email, password } = req.body;

if (!email || !password) {
return res.status(400).json({ error: 'Введіть всі поля' });
}

const user = await UserService.login(email, password);

if (!user) {
return res.status(404).json({ error: 'Помилка під час авторизації' });
}

const normalizedUser = UserService.normalize(user);
const refreshToken = jwt.generateRefreshToken(normalizedUser);

await UserService.saveToken(user.id, refreshToken);

res.cookie('refreshToken', refreshToken, {
maxAge: 30 * 24 * 60 * 60 * 1000,
httpOnly: true,
sameSite: 'none',
secure: true,
});

return res.status(200).json({
user: normalizedUser,
accessToken: jwt.generateAccessToken(normalizedUser),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend refresh logic and token issuance are correct, but there is no behavior in the frontend that redirects the user to /profile after successful login, as required by checklist item #10. After loginUser succeeds, you need to navigate to the profile page in your React components.

Comment thread src/createServer.js
Comment on lines +13 to +21
cors({
origin: process.env.CLIENT_URL,
credentials: true,
}),
);

app.use(express.json());
app.use(cookieParser());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The middleware validates the access token but doesn’t attach the decoded user to the request object. Since updateProfile in User.controller uses req.user.id and req.user.email, req.user will be undefined, causing profile updates to fail and breaking checklist items #19#21. After successful validation, assign req.user = userData before calling next().

Comment thread src/db.js
Comment on lines +13 to +21
const sequelize = new Sequelize(POSTGRES_URI, {
dialect: 'postgres',
logging: false,
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false,
},
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authMiddleware validates the access token but never attaches the decoded user object to the request, yet updateProfile in User.controller reads req.user.id and req.user.email. This will cause profile updates (name/password/email) to fail at runtime and violates checklist items #19#21. After successful validation, assign req.user = userData before next().

@mateacademy-ai-mentor mateacademy-ai-mentor left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

I am approving your solution because all core auth and profile requirements are correctly implemented and work together end-to-end. You’ve covered registration with visible and enforced password rules, activation via emailed link with a redirect to the profile page, login for non-authenticated users with proper handling of inactive accounts, logout that clears client auth state and returns the user to the login entry point, the full password reset flow (request, email-sent page, reset with password+confirmation equality, success page with link to login), and a protected profile page that can change name, password (old/new/confirmation), and email (with password verification and old-email notification). There are no critical blockers: middleware now correctly sets req.user, protected routes (like profile updates) function, and the frontend redirects and error messages behave as specified.

The remaining points are medium/low-level improvements rather than requirement violations: you could wrap public routes (home, activation, forgot, reset) with AuthGuard isPublicOnly to strictly enforce non-auth-only access, and in ProfilePage you might dispatch the updateProfile Redux thunk instead of calling authService.updateProfile directly so the global user state (e.g., name in the navbar) stays in sync and centralized success/error handling is reused. It would also be a nice UX enhancement to surface backend registration errors in RegisterModal by reading state.auth.error, similar to how you already handle login errors. Overall, you’ve done a solid job implementing a complete auth flow with good attention to edge cases and integration between backend and frontend—well done.


✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨

Footnotes

  1. Rate AI review example

Comment on lines +13 to +15
const handleNameChange = async (e: React.FormEvent) => {
e.preventDefault();
await authService.updateProfile({ name });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re calling authService.updateProfile directly in all three handlers, which successfully invokes the backend but bypasses the updateProfile Redux thunk you defined. That means the local user state in Redux won’t reflect changes (e.g., updated name) and you don’t show success/error messages from the store. Consider using dispatch(updateProfile(...)) here to keep the global auth state in sync and reuse your centralized error handling.

Comment on lines +13 to +15
const ResetPassword = () => {
const { token } = useParams<{ token: string }>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now ProfilePage calls authService.updateProfile directly, which updates the backend but leaves your Redux user state unchanged and bypasses the updateProfile thunk’s success/error handling. Since you already have a thunk that updates state.user.name and manages isLoading/error, consider dispatching updateProfile here instead so the UI stays in sync and you can show global success/errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants