diff --git a/frontend/src/api/hooks/useAuth.ts b/frontend/src/api/hooks/useAuth.ts deleted file mode 100644 index 47ee8541..00000000 --- a/frontend/src/api/hooks/useAuth.ts +++ /dev/null @@ -1,103 +0,0 @@ -// frontend/dashboard/src/api/hooks/useAuth.ts -/** - * Authentication hook with state management - */ - -import { useState, useEffect, useContext, createContext, ReactNode } from 'react'; -import { UserProfile, LoginRequest, RegisterRequest } from '../../types/api'; -import { authApi } from '../index'; -import { useAsyncAction } from './useApi'; - -interface AuthContextType { - user: UserProfile | null; - isAuthenticated: boolean; - isLoading: boolean; - login: (credentials: LoginRequest) => Promise; - register: (userData: RegisterRequest) => Promise; - logout: () => Promise; - updateProfile: (updates: Partial) => Promise; - refreshUser: () => Promise; -} - -const AuthContext = createContext(null); - -export function AuthProvider({ children }: { children: ReactNode }) { - const [user, setUser] = useState(null); - const [isLoading, setIsLoading] = useState(true); - - const { execute: executeLogin } = useAsyncAction(authApi.login.bind(authApi)); - const { execute: executeRegister } = useAsyncAction(authApi.register.bind(authApi)); - const { execute: executeLogout } = useAsyncAction(authApi.logout.bind(authApi)); - - // Initialize auth state - useEffect(() => { - const initAuth = async () => { - try { - if (authApi.isAuthenticated()) { - const profile = await authApi.getCurrentUser(); - setUser(profile); - } - } catch (error) { - // Token might be expired, clear storage - authApi.logout(); - } finally { - setIsLoading(false); - } - }; - - initAuth(); - }, []); - - const login = async (credentials: LoginRequest) => { - await executeLogin(credentials); - const profile = await authApi.getCurrentUser(); - setUser(profile); - }; - - const register = async (userData: RegisterRequest) => { - const profile = await executeRegister(userData); - setUser(profile); - }; - - const logout = async () => { - await executeLogout(); - setUser(null); - }; - - const updateProfile = async (updates: Partial) => { - const updatedProfile = await authApi.updateProfile(updates); - setUser(updatedProfile); - }; - - const refreshUser = async () => { - if (authApi.isAuthenticated()) { - const profile = await authApi.getCurrentUser(); - setUser(profile); - } - }; - - return ( - - {children} - - ); -} - -export function useAuth(): AuthContextType { - const context = useContext(AuthContext); - if (!context) { - throw new Error('useAuth must be used within an AuthProvider'); - } - return context; -} diff --git a/frontend/src/api/hooks/useTraining.ts b/frontend/src/api/hooks/useTraining.ts deleted file mode 100644 index 113fb72c..00000000 --- a/frontend/src/api/hooks/useTraining.ts +++ /dev/null @@ -1,67 +0,0 @@ -// frontend/dashboard/src/api/hooks/useTraining.ts -/** - * Training-specific hooks - */ - -import { useState, useEffect, useRef } from 'react'; -import { TrainingJobStatus, TrainingRequest } from '../../types/api'; -import { trainingApi } from '../index'; -import { useApi, useAsyncAction } from './useApi'; - -export function useTraining() { - const { data: jobs, loading, error, refetch } = useApi(() => trainingApi.getTrainingJobs()); - const { data: models, refetch: refetchModels } = useApi(() => trainingApi.getTrainedModels()); - - const { execute: startTraining, loading: startingTraining } = useAsyncAction( - trainingApi.startTraining.bind(trainingApi) - ); - - return { - jobs: jobs || [], - models: models || [], - loading, - error, - startingTraining, - startTraining: async (request: TrainingRequest) => { - const job = await startTraining(request); - await refetch(); - return job; - }, - refresh: refetch, - refreshModels: refetchModels, - }; -} - -export function useTrainingProgress(jobId: string | null) { - const [progress, setProgress] = useState(null); - const [error, setError] = useState(null); - const wsRef = useRef(null); - - useEffect(() => { - if (!jobId) return; - - // Initial status fetch - trainingApi.getTrainingStatus(jobId).then(setProgress).catch(setError); - - // Set up WebSocket for real-time updates - wsRef.current = trainingApi.subscribeToTrainingProgress( - jobId, - (updatedProgress) => { - setProgress(updatedProgress); - setError(null); - }, - (wsError) => { - setError(wsError.message); - } - ); - - return () => { - if (wsRef.current) { - wsRef.current.close(); - } - }; - }, [jobId]); - - return { progress, error }; -} - diff --git a/frontend/src/api/hooks/useWebSocket.ts b/frontend/src/api/hooks/useWebSocket.ts deleted file mode 100644 index 4306c8da..00000000 --- a/frontend/src/api/hooks/useWebSocket.ts +++ /dev/null @@ -1,109 +0,0 @@ -// frontend/dashboard/src/api/hooks/useWebSocket.ts -/** - * Generic WebSocket hook for real-time updates - */ - -import { useState, useEffect, useRef, useCallback } from 'react'; - -export interface WebSocketOptions { - reconnectAttempts?: number; - reconnectInterval?: number; - onOpen?: () => void; - onClose?: () => void; - onError?: (error: Event) => void; -} - -export function useWebSocket( - url: string | null, - options: WebSocketOptions = {} -): { - data: T | null; - connectionState: 'connecting' | 'open' | 'closed' | 'error'; - send: (data: any) => void; - close: () => void; -} { - const [data, setData] = useState(null); - const [connectionState, setConnectionState] = useState<'connecting' | 'open' | 'closed' | 'error'>('closed'); - - const wsRef = useRef(null); - const reconnectTimeoutRef = useRef(null); - const reconnectAttemptsRef = useRef(0); - - const { - reconnectAttempts = 3, - reconnectInterval = 3000, - onOpen, - onClose, - onError, - } = options; - - const connect = useCallback(() => { - if (!url || wsRef.current?.readyState === WebSocket.OPEN) return; - - try { - setConnectionState('connecting'); - wsRef.current = new WebSocket(url); - - wsRef.current.onopen = () => { - setConnectionState('open'); - reconnectAttemptsRef.current = 0; - onOpen?.(); - }; - - wsRef.current.onmessage = (event) => { - try { - const parsedData = JSON.parse(event.data); - setData(parsedData); - } catch (error) { - console.error('Failed to parse WebSocket message:', error); - } - }; - - wsRef.current.onclose = () => { - setConnectionState('closed'); - onClose?.(); - - // Attempt to reconnect - if (reconnectAttemptsRef.current < reconnectAttempts) { - reconnectAttemptsRef.current++; - reconnectTimeoutRef.current = setTimeout(connect, reconnectInterval); - } - }; - - wsRef.current.onerror = (error) => { - setConnectionState('error'); - onError?.(error); - }; - } catch (error) { - setConnectionState('error'); - console.error('WebSocket connection failed:', error); - } - }, [url, reconnectAttempts, reconnectInterval, onOpen, onClose, onError]); - - const send = useCallback((data: any) => { - if (wsRef.current?.readyState === WebSocket.OPEN) { - wsRef.current.send(JSON.stringify(data)); - } - }, []); - - const close = useCallback(() => { - if (reconnectTimeoutRef.current) { - clearTimeout(reconnectTimeoutRef.current); - } - if (wsRef.current) { - wsRef.current.close(); - } - }, []); - - useEffect(() => { - if (url) { - connect(); - } - - return () => { - close(); - }; - }, [url, connect, close]); - - return { data, connectionState, send, close }; -} \ No newline at end of file diff --git a/frontend/src/pages/dashboard/index.tsx b/frontend/src/pages/dashboard/index.tsx index e5854efe..6f443959 100644 --- a/frontend/src/pages/dashboard/index.tsx +++ b/frontend/src/pages/dashboard/index.tsx @@ -1,14 +1,15 @@ // src/pages/Dashboard/Dashboard.tsx import React, { useState, useEffect, useCallback } from 'react'; -import { useAuth } from '../../contexts/AuthContext'; +import { useAuth } from '../../api/hooks/useAuth'; import { useTrainingProgress } from '../../api/hooks/useTrainingProgress'; -import { useWebSocket } from '../../api/hooks/useWebSocket'; -import { trainingService, forecastingService, dataService } from '../../api/services'; +import { trainingApi, forecastingApi, dataApi } from '../../api'; import { TrainingProgressCard } from '../../components/training/TrainingProgressCard'; import { ForecastChart } from '../../components/charts/ForecastChart'; import { SalesUploader } from '../../components/data/SalesUploader'; import { NotificationToast } from '../../components/common/NotificationToast'; import { ErrorBoundary } from '../../components/common/ErrorBoundary'; +import { StatsCard } from '../../components/common/StatsCard'; +import { useWebSocket } from '../../api/hooks/useWebSocket'; import { ChartBarIcon, CloudArrowUpIcon,