Files
vip-coordinator/frontend/src/App.tsx
kyle dc4655cef4 Backup: 2025-06-07 19:48 - Script test
[Restore from backup: vip-coordinator-backup-2025-06-07-19-48-script-test]
2026-01-24 09:33:58 +01:00

178 lines
6.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import { apiCall } from './config/api';
import VipList from './pages/VipList';
import VipDetails from './pages/VipDetails';
import DriverList from './pages/DriverList';
import DriverDashboard from './pages/DriverDashboard';
import Dashboard from './pages/Dashboard';
import AdminDashboard from './pages/AdminDashboard';
import UserManagement from './components/UserManagement';
import Login from './components/Login';
import './App.css';
function App() {
const [user, setUser] = useState<any>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Check if user is already authenticated
const token = localStorage.getItem('authToken');
if (token) {
apiCall('/auth/me', {
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(res => {
if (res.ok) {
return res.json();
} else {
// Token is invalid, remove it
localStorage.removeItem('authToken');
throw new Error('Invalid token');
}
})
.then(userData => {
setUser(userData);
setLoading(false);
})
.catch(error => {
console.error('Auth check failed:', error);
setLoading(false);
});
} else {
setLoading(false);
}
}, []);
const handleLogin = (userData: any) => {
setUser(userData);
};
const handleLogout = () => {
localStorage.removeItem('authToken');
setUser(null);
// Optionally call logout endpoint
apiCall('/auth/logout', { method: 'POST' })
.catch(error => console.error('Logout error:', error));
};
if (loading) {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex justify-center items-center">
<div className="bg-white rounded-2xl shadow-xl p-8 flex items-center space-x-4">
<div className="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
<span className="text-lg font-medium text-slate-700">Loading VIP Coordinator...</span>
</div>
</div>
);
}
// Handle OAuth callback route even when not logged in
if (window.location.pathname === '/auth/callback' || window.location.pathname === '/auth/google/callback') {
return <Login onLogin={handleLogin} />;
}
if (!user) {
return <Login onLogin={handleLogin} />;
}
return (
<Router>
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50">
{/* Modern Navigation */}
<nav className="bg-white/80 backdrop-blur-lg border-b border-slate-200/60 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo/Brand */}
<div className="flex items-center space-x-3">
<div className="w-8 h-8 bg-gradient-to-br from-blue-600 to-indigo-600 rounded-lg flex items-center justify-center">
<span className="text-white font-bold text-sm">VC</span>
</div>
<h1 className="text-xl font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-transparent">
VIP Coordinator
</h1>
</div>
{/* Navigation Links */}
<div className="hidden md:flex items-center space-x-1">
<Link
to="/"
className="px-4 py-2 text-sm font-medium text-slate-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200"
>
Dashboard
</Link>
<Link
to="/vips"
className="px-4 py-2 text-sm font-medium text-slate-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200"
>
VIPs
</Link>
<Link
to="/drivers"
className="px-4 py-2 text-sm font-medium text-slate-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200"
>
Drivers
</Link>
{(user.role === 'administrator' || user.role === 'coordinator') && (
<Link
to="/admin"
className="px-4 py-2 text-sm font-medium text-slate-700 hover:text-amber-600 hover:bg-amber-50 rounded-lg transition-all duration-200"
>
Admin
</Link>
)}
{user.role === 'administrator' && (
<Link
to="/users"
className="px-4 py-2 text-sm font-medium text-slate-700 hover:text-purple-600 hover:bg-purple-50 rounded-lg transition-all duration-200"
>
Users
</Link>
)}
</div>
{/* User Menu */}
<div className="flex items-center space-x-4">
<div className="hidden sm:flex items-center space-x-3">
<div className="w-8 h-8 bg-gradient-to-br from-slate-400 to-slate-600 rounded-full flex items-center justify-center">
<span className="text-white text-xs font-medium">
{user.name.charAt(0).toUpperCase()}
</span>
</div>
<div className="text-sm">
<div className="font-medium text-slate-900">{user.name}</div>
<div className="text-slate-500 capitalize">{user.role}</div>
</div>
</div>
<button
onClick={handleLogout}
className="bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl"
>
Logout
</button>
</div>
</div>
</div>
</nav>
{/* Main Content */}
<main className="max-w-7xl mx-auto px-6 lg:px-8 py-8">
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/vips" element={<VipList />} />
<Route path="/vips/:id" element={<VipDetails />} />
<Route path="/drivers" element={<DriverList />} />
<Route path="/drivers/:driverId" element={<DriverDashboard />} />
<Route path="/admin" element={<AdminDashboard />} />
<Route path="/users" element={<UserManagement currentUser={user} />} />
</Routes>
</main>
</div>
</Router>
);
}
export default App;