import React, { useState, useEffect } from 'react';
import { login, loginLocal, getUserStatus, logout, getAuthConfig } from '../../../services/apiService';
const LoginPage = () => {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [oidcEnabled, setOidcEnabled] = useState(false);
// Local login state
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [successMessage, setSuccessMessage] = useState('');
useEffect(() => {
// 1. Check if OIDC is enabled
const checkConfig = async () => {
try {
const config = await getAuthConfig();
setOidcEnabled(config.oidc_configured);
} catch (err) {
console.error("Failed to fetch auth config", err);
}
};
checkConfig();
// 2. Handle OIDC callback or persistent session
const params = new URLSearchParams(window.location.search);
const userIdFromUrl = params.get('user_id');
const storedUserId = localStorage.getItem('userId');
const userId = userIdFromUrl || storedUserId;
const isLinked = params.get("linked") === "true";
if (userId) {
setIsLoading(true);
const fetchUserDetails = async () => {
try {
const userStatus = await getUserStatus(userId);
setUser(userStatus);
localStorage.setItem('userId', userStatus.id);
window.history.replaceState({}, document.title, window.location.pathname);
if (isLinked) {
setSuccessMessage("Social identity successfully linked to your existing account!");
}
setTimeout(() => {
window.location.href = "/swarm-control";
}, 1500);
} catch (err) {
setError('Failed to get user status. Please try again.');
localStorage.removeItem('userId'); // Clear invalid ID
console.error(err);
} finally {
setIsLoading(false);
}
};
fetchUserDetails();
}
}, []);
const handleOidcLogin = () => {
login();
};
const handleLocalLogin = async (e) => {
e.preventDefault();
setIsLoading(true);
setError(null);
try {
const result = await loginLocal(email, password);
setUser({ id: result.user_id, email: result.email });
localStorage.setItem('userId', result.user_id);
// Redirect to home page after successful local login
setTimeout(() => {
window.location.href = "/";
}, 1000);
} catch (err) {
setError(err.message || 'Login failed. Please check your credentials.');
} finally {
setIsLoading(false);
}
};
const handleLogout = async () => {
setIsLoading(true);
try {
await logout();
localStorage.removeItem('userId');
setUser(null);
setError(null);
} catch (err) {
setError('Failed to log out. Please try again.');
console.error(err);
} finally {
setIsLoading(false);
}
};
const renderContent = () => {
if (isLoading) {
return (
<div className="flex flex-col items-center justify-center p-4">
<svg className="animate-spin h-10 w-10 mb-4 text-blue-500" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span className="text-gray-600 dark:text-gray-400 font-medium">Authenticating...</span>
</div>
);
}
if (user) {
return (
<div className="p-4 text-center">
<div className="mb-4 inline-flex items-center justify-center w-16 h-16 bg-green-100 dark:bg-green-900 rounded-full">
<svg className="w-8 h-8 text-green-600 dark:text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
<h3 className="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Welcome Back!</h3>
<p className="text-gray-600 dark:text-gray-400 mb-6">{user.email}</p>
<button
onClick={handleLogout}
className="w-full bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-4 rounded-lg transition-all shadow-md focus:outline-none focus:ring-2 focus:ring-red-500"
>
Sign Out
</button>
</div>
);
}
return (
<div className="w-full">
<div className="mb-8">
<h2 className="text-3xl font-extrabold text-gray-900 dark:text-white mb-2">Sign In</h2>
<p className="text-gray-500 dark:text-gray-400">Access your Cortex Hub dashboard</p>
</div>
{error && (
<div className="mb-6 p-4 bg-red-50 dark:bg-red-900/30 border-l-4 border-red-500 text-red-700 dark:text-red-400 text-sm text-left">
{error}
</div>
)}
{successMessage && (
<div className="mb-6 p-4 bg-green-50 dark:bg-green-900/30 border-l-4 border-green-500 text-green-700 dark:text-green-400 text-sm text-left">
{successMessage}
</div>
)}
<form onSubmit={handleLocalLogin} className="space-y-4 text-left">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email Address</label>
<input
type="email"
id="email"
name="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-4 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:text-white transition-all"
placeholder="admin@example.com"
autoComplete="email"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Password</label>
<input
type="password"
id="password"
name="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-4 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:text-white transition-all"
placeholder="••••••••"
autoComplete="current-password"
/>
</div>
<button
type="submit"
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg shadow-lg hover:shadow-xl transition-all transform hover:-translate-y-0.5 active:translate-y-0"
>
Sign In with Password
</button>
</form>
{oidcEnabled && (
<div className="mt-8">
<div className="relative flex items-center justify-center mb-6">
<div className="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
<span className="flex-shrink mx-4 text-gray-400 text-sm uppercase font-bold tracking-wider">Or</span>
<div className="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
</div>
<button
onClick={handleOidcLogin}
className="w-full flex items-center justify-center bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-white font-semibold py-3 px-4 rounded-lg shadow-sm transition-all"
>
<svg className="w-5 h-5 mr-3" viewBox="0 0 48 48">
<path fill="#FFC107" d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"></path>
<path fill="#FF3D00" d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"></path>
<path fill="#4CAF50" d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"></path>
<path fill="#1976D2" d="M43.611,20.083L43.611,20.083L42,20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"></path>
</svg>
Sign in with SSO
</button>
</div>
)}
</div>
);
};
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-black p-4">
<div className="bg-white dark:bg-gray-800 p-10 rounded-2xl shadow-2xl max-w-md w-full border border-gray-100 dark:border-gray-700">
<div className="mb-10 flex justify-center">
<div className="w-16 h-16 bg-blue-600 rounded-xl flex items-center justify-center shadow-lg transform -rotate-6">
<span className="text-white text-3xl font-black">C</span>
</div>
</div>
{renderContent()}
</div>
</div>
);
};
export default LoginPage;