Files
hMarket/frontend/src/contexts/AuthContext.tsx
2026-02-03 01:22:08 +03:00

205 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { createContext, useContext, useReducer, useEffect, ReactNode } from 'react';
import { authAPI } from '../services/api';
import { User } from '../types';
interface AuthState {
user: User | null;
token: string | null;
isLoading: boolean;
isAuthenticated: boolean;
}
interface AuthContextType extends AuthState {
login: (login: string, password: string) => Promise<void>;
register: (userData: RegisterData) => Promise<void>;
logout: () => void;
updateProfile: (userData: Partial<User>) => Promise<void>;
changePassword: (currentPassword: string, newPassword: string) => Promise<void>;
handleGoogleCallback: (token: string) => Promise<void>;
}
interface RegisterData {
firstName: string;
lastName: string;
email: string;
password: string;
confirmPassword: string;
}
type AuthAction =
| { type: 'SET_LOADING'; payload: boolean }
| { type: 'SET_USER'; payload: { user: User; token: string } }
| { type: 'CLEAR_USER' }
| { type: 'UPDATE_USER'; payload: Partial<User> };
const initialState: AuthState = {
user: null,
token: localStorage.getItem('token'),
isLoading: true,
isAuthenticated: false,
};
const authReducer = (state: AuthState, action: AuthAction): AuthState => {
switch (action.type) {
case 'SET_LOADING':
return { ...state, isLoading: action.payload };
case 'SET_USER':
return {
...state,
user: action.payload.user,
token: action.payload.token,
isAuthenticated: true,
isLoading: false,
};
case 'CLEAR_USER':
return {
...state,
user: null,
token: null,
isAuthenticated: false,
isLoading: false,
};
case 'UPDATE_USER':
return {
...state,
user: state.user ? { ...state.user, ...action.payload } : null,
};
default:
return state;
}
};
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const useAuth = () => {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
interface AuthProviderProps {
children: ReactNode;
}
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
// Backend'ten gelen kullanıcı nesnesini frontend User tipine dönüştür
const normalizeUser = (apiUser: any): User => {
return {
id: apiUser.id,
firstName: apiUser.firstName,
lastName: apiUser.lastName,
email: apiUser.email,
role: apiUser.isAdmin ? 'ADMIN' : 'USER',
isActive: apiUser.isActive ?? true,
avatar: apiUser.avatar ?? undefined,
createdAt: apiUser.createdAt,
updatedAt: apiUser.updatedAt ?? apiUser.createdAt,
settings: apiUser.settings ?? undefined,
};
};
// Check if user is authenticated on app load
useEffect(() => {
const checkAuth = async () => {
const token = localStorage.getItem('token');
if (token) {
try {
const response = await authAPI.getProfile();
dispatch({
type: 'SET_USER',
payload: { user: normalizeUser(response.data.data.user), token },
});
} catch (error) {
localStorage.removeItem('token');
dispatch({ type: 'CLEAR_USER' });
}
} else {
dispatch({ type: 'SET_LOADING', payload: false });
}
};
checkAuth();
}, []);
const login = async (login: string, password: string) => {
dispatch({ type: 'SET_LOADING', payload: true });
try {
const response = await authAPI.login({ login, password });
const { user, token } = response.data.data;
localStorage.setItem('token', token);
dispatch({ type: 'SET_USER', payload: { user: normalizeUser(user), token } });
} catch (error) {
dispatch({ type: 'SET_LOADING', payload: false });
throw error;
}
};
const register = async (userData: RegisterData) => {
dispatch({ type: 'SET_LOADING', payload: true });
try {
const response = await authAPI.register(userData);
const { user, token } = response.data.data;
localStorage.setItem('token', token);
dispatch({ type: 'SET_USER', payload: { user: normalizeUser(user), token } });
} catch (error) {
dispatch({ type: 'SET_LOADING', payload: false });
throw error;
}
};
const logout = () => {
localStorage.removeItem('token');
dispatch({ type: 'CLEAR_USER' });
};
const updateProfile = async (userData: Partial<User>) => {
try {
const response = await authAPI.updateProfile(userData);
dispatch({ type: 'UPDATE_USER', payload: normalizeUser(response.data.data.user) });
} catch (error) {
throw error;
}
};
const changePassword = async (currentPassword: string, newPassword: string) => {
try {
await authAPI.changePassword({ currentPassword, newPassword });
} catch (error) {
throw error;
}
};
const handleGoogleCallback = async (token: string) => {
dispatch({ type: 'SET_LOADING', payload: true });
try {
localStorage.setItem('token', token);
const response = await authAPI.getProfile();
dispatch({
type: 'SET_USER',
payload: { user: normalizeUser(response.data.data.user), token },
});
} catch (error) {
localStorage.removeItem('token');
dispatch({ type: 'SET_LOADING', payload: false });
throw error;
}
};
const value: AuthContextType = {
...state,
login,
register,
logout,
updateProfile,
changePassword,
handleGoogleCallback,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};