From 832f5c0a636b5bb7e4d25a8d8f3ca3741f6cba3e Mon Sep 17 00:00:00 2001 From: CCS Admin Date: Thu, 26 Feb 2026 09:42:46 +0000 Subject: [PATCH] feat: add profile/avatar/MFA types and service functions Extend User and UserResponse interfaces with first_name, last_name, display_name, avatar_url fields. Add ProfileUpdatePayload, ChangePasswordPayload, MFASetupResponse, MFAVerifyPayload types. Add authService functions for profile update, avatar upload/delete, password change, and MFA setup/verify/disable. Add refreshUser to AuthContext so components can re-fetch user data after changes. Co-Authored-By: Claude Opus 4.6 --- frontend/src/context/AuthContext.tsx | 7 +++++ frontend/src/services/authService.ts | 38 +++++++++++++++++++++++++++- frontend/src/types/index.ts | 32 +++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/frontend/src/context/AuthContext.tsx b/frontend/src/context/AuthContext.tsx index a16fb30..a51faaf 100644 --- a/frontend/src/context/AuthContext.tsx +++ b/frontend/src/context/AuthContext.tsx @@ -9,6 +9,7 @@ interface AuthContextType { login: (data: LoginRequest) => Promise register: (data: RegisterRequest) => Promise logout: () => Promise + refreshUser: () => Promise } const AuthContext = createContext(null) @@ -46,6 +47,11 @@ export function AuthProvider({ children }: { children: ReactNode }) { setUser(null) } + const refreshUserFn = async () => { + const updatedUser = await authService.getMe() + setUser(updatedUser) + } + return ( {children} diff --git a/frontend/src/services/authService.ts b/frontend/src/services/authService.ts index 94c241e..07db2fe 100644 --- a/frontend/src/services/authService.ts +++ b/frontend/src/services/authService.ts @@ -1,5 +1,5 @@ import api from './api' -import type { LoginRequest, RegisterRequest, TokenResponse, User } from '@/types' +import type { LoginRequest, RegisterRequest, TokenResponse, User, ProfileUpdatePayload, ChangePasswordPayload, MFASetupResponse, MFAVerifyPayload } from '@/types' export async function login(data: LoginRequest): Promise { const response = await api.post('/auth/login', data) @@ -29,3 +29,39 @@ export async function getMe(): Promise { const response = await api.get('/auth/me') return response.data } + +export async function updateProfile(data: ProfileUpdatePayload): Promise { + const response = await api.put('/auth/profile', data) + return response.data +} + +export async function uploadAvatar(file: File): Promise { + const formData = new FormData() + formData.append('file', file) + const response = await api.post('/auth/avatar', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }) + return response.data +} + +export async function deleteAvatar(): Promise { + const response = await api.delete('/auth/avatar') + return response.data +} + +export async function changePassword(data: ChangePasswordPayload): Promise { + await api.post('/auth/change-password', data) +} + +export async function setupMFA(): Promise { + const response = await api.post('/auth/mfa/setup') + return response.data +} + +export async function verifyMFA(data: MFAVerifyPayload): Promise { + await api.post('/auth/mfa/verify', data) +} + +export async function disableMFA(password: string): Promise { + await api.delete('/auth/mfa', { data: { password } }) +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index e05494e..390d80d 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -2,6 +2,10 @@ export interface User { id: number username: string email: string + first_name: string | null + last_name: string | null + display_name: string | null + avatar_url: string | null role: 'admin' | 'dak_mitarbeiter' mfa_enabled: boolean is_active: boolean @@ -196,6 +200,10 @@ export interface UserResponse { id: number username: string email: string + first_name: string | null + last_name: string | null + display_name: string | null + avatar_url: string | null role: 'admin' | 'dak_mitarbeiter' is_active: boolean mfa_enabled: boolean @@ -217,6 +225,30 @@ export interface UpdateUserPayload { is_active?: boolean } +// Account / Profile +export interface ProfileUpdatePayload { + first_name?: string + last_name?: string + display_name?: string + username?: string + email?: string +} + +export interface ChangePasswordPayload { + old_password: string + new_password: string +} + +export interface MFASetupResponse { + secret: string + qr_uri: string +} + +export interface MFAVerifyPayload { + secret: string + code: string +} + // Admin Invitations export interface InvitationResponse { id: number