diff --git a/backend/src/controllers/v2/usersController.ts b/backend/src/controllers/v2/usersController.ts index 19d88d56d..b5849f2e7 100644 --- a/backend/src/controllers/v2/usersController.ts +++ b/backend/src/controllers/v2/usersController.ts @@ -3,10 +3,10 @@ import { Types } from "mongoose"; import crypto from "crypto"; import bcrypt from "bcrypt"; import { - MembershipOrg, - User, APIKeyData, - TokenVersion + MembershipOrg, + TokenVersion, + User } from "../../models"; import { getSaltRounds } from "../../config"; @@ -80,6 +80,26 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => { }); } +/** + * Update the current user's name [firstName, lastName]. + * @param req + * @param res + * @returns + */ +export const updateName = async (req: Request, res: Response) => { + const { firstName, lastName }: { firstName: string; lastName: string; } = req.body; + req.user.firstName = firstName; + req.user.lastName = lastName || ""; + + await req.user.save(); + + const user = req.user; + + return res.status(200).send({ + user, + }); +} + /** * Return organizations that the current user is part of. * @param req diff --git a/backend/src/routes/v2/users.ts b/backend/src/routes/v2/users.ts index f5a1401dc..7815513ec 100644 --- a/backend/src/routes/v2/users.ts +++ b/backend/src/routes/v2/users.ts @@ -29,6 +29,16 @@ router.patch( usersController.updateMyMfaEnabled ); +router.patch( + "/me/name", + requireAuth({ + acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY], + }), + body("firstName").exists(), + validateRequest, + usersController.updateName +); + router.get( "/me/organizations", requireAuth({ diff --git a/frontend/src/hooks/api/users/queries.tsx b/frontend/src/hooks/api/users/queries.tsx index c1bcf43a2..fe9efd73f 100644 --- a/frontend/src/hooks/api/users/queries.tsx +++ b/frontend/src/hooks/api/users/queries.tsx @@ -16,6 +16,7 @@ import { CreateAPIKeyRes, DeletOrgMembershipDTO, OrgUser, + RenameUserDTO, TokenVersion, UpdateOrgUserRoleDTO, User} from "./types"; @@ -45,6 +46,18 @@ const fetchUserAction = async (action: string) => { return data.userAction; }; +export const useRenameUser = () => { + const queryClient = useQueryClient(); + + return useMutation<{}, {}, RenameUserDTO>({ + mutationFn: ({ newName }) => + apiRequest.patch("/api/v2/users/me/name", { firstName: newName?.split(" ")[0], lastName: newName?.split(" ").slice(1).join(" ") }), + onSuccess: () => { + queryClient.invalidateQueries(userKeys.getUser); + } + }); +}; + export const useGetUserAction = (action: string) => useQuery({ queryKey: userKeys.userAction, diff --git a/frontend/src/hooks/api/users/types.ts b/frontend/src/hooks/api/users/types.ts index e272e7bfd..31a33c756 100644 --- a/frontend/src/hooks/api/users/types.ts +++ b/frontend/src/hooks/api/users/types.ts @@ -67,6 +67,10 @@ export type CreateAPIKeyRes = { apiKeyData: APIKeyData; } +export type RenameUserDTO = { + newName: string; +}; + export type APIKeyData = { _id: string; name: string; diff --git a/frontend/src/views/Settings/PersonalSettingsPage/ChangeLanguageSection/ChangeLanguageSection.tsx b/frontend/src/views/Settings/PersonalSettingsPage/ChangeLanguageSection/ChangeLanguageSection.tsx index 7d2dfb604..c64050c41 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/ChangeLanguageSection/ChangeLanguageSection.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/ChangeLanguageSection/ChangeLanguageSection.tsx @@ -15,7 +15,7 @@ export const ChangeLanguageSection = () => { }; return ( -
{t("settings.personal.change-language")}
diff --git a/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/PersonalSecurityTab.tsx b/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/PersonalSecurityTab.tsx index 4c0b21454..abfec566a 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/PersonalSecurityTab.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/PersonalSecurityTab.tsx @@ -3,10 +3,12 @@ import { ChangePasswordSection } from "../ChangePasswordSection"; import { EmergencyKitSection } from "../EmergencyKitSection"; import { SecuritySection } from "../SecuritySection"; import { SessionsSection } from "../SessionsSection"; +import { UserNameSection } from "../UserNameSection"; export const PersonalSecurityTab = () => { return ({t("settings.project.title")}