Files
usda-vision/management-dashboard-web-app/src/App.tsx

166 lines
4.8 KiB
TypeScript
Executable File

import { useState, useEffect } from 'react'
import { supabase, userManagement } from './lib/supabase'
import { Login } from './components/Login'
import { Dashboard } from './components/Dashboard'
import { CameraRoute } from './components/CameraRoute'
function App() {
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null)
const [loading, setLoading] = useState(true)
const [currentRoute, setCurrentRoute] = useState(window.location.pathname)
useEffect(() => {
// Check initial auth state
checkAuthState()
// Listen for auth changes
const { data: { subscription } } = supabase.auth.onAuthStateChange((event: string, session: any) => {
console.log('Auth state changed:', event, !!session)
setIsAuthenticated(!!session)
setLoading(false)
// Sync OAuth user on successful sign in (creates user profile if needed)
if ((event === 'SIGNED_IN' || event === 'INITIAL_SESSION') && session) {
userManagement.syncOAuthUser().catch((err) => {
console.error('Failed to sync OAuth user:', err)
})
}
// Handle signout route
if (event === 'SIGNED_OUT') {
setCurrentRoute('/')
window.history.pushState({}, '', '/')
}
})
// Handle browser navigation
const handlePopState = () => {
setCurrentRoute(window.location.pathname)
}
window.addEventListener('popstate', handlePopState)
return () => {
subscription.unsubscribe()
window.removeEventListener('popstate', handlePopState)
}
}, [])
useEffect(() => {
// Handle signout route
if (currentRoute === '/signout') {
handleLogout()
}
}, [currentRoute])
const checkAuthState = async () => {
try {
const { data: { session } } = await supabase.auth.getSession()
setIsAuthenticated(!!session)
} catch (error) {
console.error('Error checking auth state:', error)
setIsAuthenticated(false)
} finally {
setLoading(false)
}
}
const handleLoginSuccess = () => {
setIsAuthenticated(true)
setCurrentRoute('/')
window.history.pushState({}, '', '/')
}
const handleLogout = async () => {
try {
// Clear Supabase session
await supabase.auth.signOut()
// Clear any local storage items
localStorage.removeItem('supabase.auth.token')
localStorage.removeItem('dashboard-current-view')
// Reset state
setIsAuthenticated(false)
setCurrentRoute('/')
window.history.pushState({}, '', '/')
} catch (error) {
console.error('Logout error:', error)
// Still reset state even if there's an error
localStorage.removeItem('dashboard-current-view')
setIsAuthenticated(false)
setCurrentRoute('/')
window.history.pushState({}, '', '/')
}
}
// Check if current route is a camera route (no authentication required)
const isCameraRoute = (route: string) => {
const cameraRoutePattern = /^\/camera(\d+)$/
return cameraRoutePattern.test(route)
}
// Extract camera number from route
const getCameraNumber = (route: string) => {
const match = route.match(/^\/camera(\d+)$/)
return match ? `camera${match[1]}` : null
}
// Check if current route is a scheduling sub-route
const isSchedulingRoute = (route: string) => {
return route.startsWith('/scheduling')
}
// Extract scheduling sub-route
const getSchedulingSubRoute = (route: string) => {
if (route === '/scheduling') {
return 'main'
}
const match = route.match(/^\/scheduling\/(.+)$/)
return match ? match[1] : 'main'
}
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-indigo-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading...</p>
</div>
</div>
)
}
// Handle signout route
if (currentRoute === '/signout') {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-indigo-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Signing out...</p>
</div>
</div>
)
}
// Handle camera routes (no authentication required)
if (isCameraRoute(currentRoute)) {
const cameraNumber = getCameraNumber(currentRoute)
if (cameraNumber) {
return <CameraRoute cameraNumber={cameraNumber} />
}
}
return (
<>
{isAuthenticated ? (
<Dashboard onLogout={handleLogout} currentRoute={currentRoute} />
) : (
<Login onLoginSuccess={handleLoginSuccess} />
)}
</>
)
}
export default App