Add new frontend

This commit is contained in:
Urtzi Alfaro
2025-07-22 07:37:51 +02:00
parent d77cbbafb6
commit 777798b054
24 changed files with 2023 additions and 2590 deletions

View File

@@ -1,30 +1,20 @@
import React, { createContext, useState, useContext, useEffect } from 'react';
import api from '../api/api';
import { useRouter } from 'next/router';
import axios from 'axios';
interface User {
id: string;
email: string;
full_name: string;
tenant_id: string;
}
interface Tenant {
id: string;
name: string;
subdomain: string;
}
// src/contexts/AuthContext.tsx
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import { authService, UserProfile } from '../api/auth/authService';
import { tokenManager } from '../api/auth/tokenManager';
interface AuthContextType {
user: User | null;
tenant: Tenant | null;
user: UserProfile | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
loading: boolean;
register: (data: any) => Promise<void>;
logout: () => Promise<void>;
updateProfile: (updates: Partial<UserProfile>) => Promise<void>;
refreshUser: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
const AuthContext = createContext<AuthContextType | null>(null);
export const useAuth = () => {
const context = useContext(AuthContext);
@@ -35,74 +25,86 @@ export const useAuth = () => {
};
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);
const [tenant, setTenant] = useState<Tenant | null>(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
const [user, setUser] = useState<UserProfile | null>(null);
const [isLoading, setIsLoading] = useState(true);
// Initialize auth state
useEffect(() => {
const token = localStorage.getItem('access_token');
if (token) {
loadUserData();
} else {
setLoading(false);
const initAuth = async () => {
try {
await tokenManager.initialize();
if (authService.isAuthenticated()) {
const profile = await authService.getCurrentUser();
setUser(profile);
}
} catch (error) {
console.error('Auth initialization failed:', error);
} finally {
setIsLoading(false);
}
};
initAuth();
}, []);
const login = useCallback(async (email: string, password: string) => {
const profile = await authService.login({ email, password });
setUser(profile);
}, []);
const register = useCallback(async (data: any) => {
const profile = await authService.register(data);
setUser(profile);
}, []);
const logout = useCallback(async () => {
await authService.logout();
setUser(null);
}, []);
const updateProfile = useCallback(async (updates: Partial<UserProfile>) => {
const updated = await authService.updateProfile(updates);
setUser(updated);
}, [updateProfile]);
const refreshUser = useCallback(async () => {
if (authService.isAuthenticated()) {
const profile = await authService.getCurrentUser();
setUser(profile);
}
}, []);
const loadUserData = async () => {
try {
const response = await api.get('/auth/users/me');
setUser(response.data.user);
setTenant(response.data.tenant);
} catch (error) {
console.error('Failed to load user data:', error);
localStorage.removeItem('access_token');
localStorage.removeItem('tenant_id');
setUser(null);
setTenant(null);
} finally {
setLoading(false);
}
};
// Set up token refresh interval
useEffect(() => {
if (!user) return;
const login = async (email: string, password: string) => {
try {
// Create form data for OAuth2PasswordRequestForm
const formData = new URLSearchParams();
formData.append('username', email);
formData.append('password', password);
// Check token expiry every minute
const interval = setInterval(async () => {
try {
await tokenManager.getAccessToken(); // This will refresh if needed
} catch (error) {
console.error('Token refresh failed:', error);
await logout();
}
}, 60000); // 1 minute
// Make login request with correct content type
const response = await axios.post(
`${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'}/api/v1/auth/token`,
formData,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
localStorage.setItem('access_token', response.data.access_token);
localStorage.setItem('tenant_id', response.data.tenant_id);
await loadUserData();
} catch (error) {
console.error('Login failed:', error);
throw error;
}
};
const logout = () => {
localStorage.removeItem('access_token');
localStorage.removeItem('tenant_id');
setUser(null);
setTenant(null);
router.push('/login');
};
return () => clearInterval(interval);
}, [user, logout]);
return (
<AuthContext.Provider value={{ user, tenant, login, logout, loading }}>
<AuthContext.Provider
value={{
user,
isAuthenticated: !!user,
isLoading,
login,
register,
logout,
updateProfile,
refreshUser
}}
>
{children}
</AuthContext.Provider>
);