Skip to content

feat: add user settings page with name and password update#130

Open
akhilachiju wants to merge 15 commits into
mainfrom
feat/issue-114/user-settings-page
Open

feat: add user settings page with name and password update#130
akhilachiju wants to merge 15 commits into
mainfrom
feat/issue-114/user-settings-page

Conversation

@akhilachiju

@akhilachiju akhilachiju commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Resolves #114

A logged-in user can update their display name and password from the settings page. The form integrates with the existing backend user service via httpOnly cookies for authentication.

What changed

  • Added UserSettingsForm component with first name, last name, and password fields — pre-filled with the logged-in user's current name
  • Added settings/page.tsx and settings/layout.tsx under the private route
  • Added Settings entry to the UserArea dropdown with a GearSixIcon
  • Added updateUserSchema and UpdateUserInput to validators/schemas.ts, reusing the shared passwordSchema already used in registration
  • Update logic is handled locally in the settings page (not in useAuth)
  • Auth context is updated on save so the header name reflects changes immediately
  • Form skips the API call if nothing has changed
  • Removed tokenStorage — authentication is handled exclusively via httpOnly cookies
  • Added translations for the Settings menu entry (en.json, de.json)

Why

The app already uses httpOnly cookies for authentication set on login. No token storage in localStorage is needed or appropriate — the browser sends the cookie automatically on every request. Password
validation reuses the existing passwordSchema to stay consistent with registration rules.

Acceptance Criteria

  • PUT /users/:id updates the user's first name and last name
  • Password is updated only when a new password is provided
  • Leaving the password fields blank keeps the existing password
  • New password must meet the same strength requirements as registration
  • Response does not expose passwordHash
  • Settings page shows a success message on save
  • Settings page shows an error message if the request fails
  • First name and last name are pre-filled with the current user's values and persist after refresh
  • No API call is made if the user submits without making any changes
  • Header name updates immediately after saving without a page refresh

@vercel

vercel Bot commented Jun 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
redi-events-api Ready Ready Preview, Comment Jun 17, 2026 10:29am
redi-events-frontend Ready Ready Preview, Comment Jun 17, 2026 10:29am
redi-events-storybook Ready Ready Preview, Comment Jun 17, 2026 10:29am

Comment thread frontend/src/utils/tokenStorage.ts Outdated
Comment on lines +1 to +26
const TOKEN_KEY = 'token';

export const tokenStorage = {
set(token: string): void {
localStorage.setItem(TOKEN_KEY, token);
},

get(): string | null {
return localStorage.getItem(TOKEN_KEY);
},

remove(): void {
localStorage.removeItem(TOKEN_KEY);
},

getUserId(): string | null {
const token = this.get();
if (!token) return null;
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.userId ?? null;
} catch {
return null;
}
},
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we are using a cookies approach to authentication, aren't we?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, exactly! We're using httpOnly cookies. I also removed a leftover tokenStorage.set(data.token) line that was still in authService.ts from an old token-based approach — that's cleaned up now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If you're using cookies you do not need token storage, cookies are already stored by the browser.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done — tokenStorage.ts has been deleted entirely. We're using httpOnly cookies exclusively now, no local storage involved.

@santi-b-b

Copy link
Copy Markdown
Collaborator

Hi! I tried to update a users password and I received an error. It seems like in the backend the update user is not implemented to change the password. is that right?

async updateUser(id: string, data: { email?: string; firstName?: string; lastName?: string }) {
→ 43 const user = await prisma.user.update({
where: {
id: "d9a8b550-69d3-4ec4-9d92-080564d7420f"
},
data: {
firstName: "Santiago",
lastName: "Benitez",
password: "Hola.1234",
~~~~~~~~
? id?: String | StringFieldUpdateOperationsInput,
? email?: String | StringFieldUpdateOperationsInput,
? passwordHash?: String | StringFieldUpdateOperationsInput,
? role?: Role | EnumRoleFieldUpdateOperationsInput,
? createdAt?: DateTime | DateTimeFieldUpdateOperationsInput,
? updatedAt?: DateTime | DateTimeFieldUpdateOperationsInput,
? events?: EventUpdateManyWithoutOrganizerNestedInput,
? attendances?: AttendanceUpdateManyWithoutUserNestedInput
}
})

@akhilachiju

Copy link
Copy Markdown
Collaborator Author

Hi! I tried to update a users password and I received an error. It seems like in the backend the update user is not implemented to change the password. is that right?

async updateUser(id: string, data: { email?: string; firstName?: string; lastName?: string }) { → 43 const user = await prisma.user.update({ where: { id: "d9a8b550-69d3-4ec4-9d92-080564d7420f" }, data: { firstName: "Santiago", lastName: "Benitez", password: "Hola.1234", ~~~~~~~~ ? id?: String | StringFieldUpdateOperationsInput, ? email?: String | StringFieldUpdateOperationsInput, ? passwordHash?: String | StringFieldUpdateOperationsInput, ? role?: Role | EnumRoleFieldUpdateOperationsInput, ? createdAt?: DateTime | DateTimeFieldUpdateOperationsInput, ? updatedAt?: DateTime | DateTimeFieldUpdateOperationsInput, ? events?: EventUpdateManyWithoutOrganizerNestedInput, ? attendances?: AttendanceUpdateManyWithoutUserNestedInput } })

Good catch! The updateUser service method wasn't accepting a password field, and even if it had, it was passing it directly to Prisma which expects passwordHash. Fixed it to hash the password with bcrypt before storing it.

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/fabio-rodrigues-projects-d0d7c49d?upgradeToPro=build-rate-limit

@FrontierPsychiatrist FrontierPsychiatrist left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The token storage must be removed. You should not just casually store sensitive credentials in local storage in a PR about editing user data, especially when there is already an authentication system (cookies) in place.

The updateUser function must not be in the auth` hook, it is no authentication feature.

Comment thread backend/src/services/userService.ts Outdated
}

async updateUser(id: string, data: { email?: string; firstName?: string; lastName?: string }) {
async updateUser(id: string, data: { email?: string; firstName?: string; lastName?: string; password?: string }) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Avoid inline types like this when they get to large, it is better to extract a type for this. It's hard to read.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done.

Comment thread frontend/src/hooks/useAuth.ts Outdated
}
};

const updateUser = async ({ firstName, lastName, newPassword }: UpdateUserInput) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

That is no authentication issue and should not be in this hook. It's also wrongly reusing state from the auth hook. Please move this to a different hook, or just keep it local to where it is needed (I don't assume the user will be updated all over the application?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done — updateUser has been moved out of useAuth and inlined directly into the settings page, since it's only used there. No separate hook needed.

headers: {
'Content-Type': 'application/json',
},
headers: { 'Content-Type': 'application/json' },

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please avoid unnecessary formatting changes in your PRs. This is not related to editing the user.

@akhilachiju akhilachiju Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Noted.

Comment thread frontend/src/utils/tokenStorage.ts Outdated
@@ -0,0 +1,26 @@
const TOKEN_KEY = 'token';

export const tokenStorage = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please remove this complete file, it is not related to the user editing and a substantial add to the authentication system.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done.

Comment thread frontend/src/utils/tokenStorage.ts Outdated
Comment on lines +1 to +26
const TOKEN_KEY = 'token';

export const tokenStorage = {
set(token: string): void {
localStorage.setItem(TOKEN_KEY, token);
},

get(): string | null {
return localStorage.getItem(TOKEN_KEY);
},

remove(): void {
localStorage.removeItem(TOKEN_KEY);
},

getUserId(): string | null {
const token = this.get();
if (!token) return null;
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.userId ?? null;
} catch {
return null;
}
},
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If you're using cookies you do not need token storage, cookies are already stored by the browser.

@akhilachiju

akhilachiju commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

The token storage must be removed. You should not just casually store sensitive credentials in local storage in a PR about editing user data, especially when there is already an authentication system (cookies) in place.

The updateUser function must not be in the auth` hook, it is no authentication feature.

tokenStorage.ts has been deleted — we're relying solely on httpOnly cookies now. updateUser has been moved out of useAuth and inlined directly into the settings page, since it's only used there.


<div className="flex flex-col gap-4">
<InputField
label="First name"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hi, I think that could be nice to have currently first name as default value.
Imaging that the user only want to change the password, then its unnecessary to tip first name and last name again.

Image

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good point! I've added default values for first name and last name from the logged-in user, so they're pre-filled in the form. Only the password fields stay empty.

@wms198

wms198 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Hey, I checked just now and it looks good. Thank you that I learn a lot from your code.

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.

User settings page

4 participants