Spaces:
Sleeping
Sleeping
| import React, { useState } from 'react'; | |
| import axios from 'axios'; | |
| import { motion } from 'framer-motion'; | |
| import { FaLock, FaSpinner } from 'react-icons/fa'; | |
| const LoginScreen = ({ onAuthenticated }) => { | |
| const [password, setPassword] = useState(''); | |
| const [loading, setLoading] = useState(false); | |
| const [error, setError] = useState(null); | |
| const handleSubmit = async (e) => { | |
| e.preventDefault(); | |
| if (!password.trim()) { | |
| setError('Please enter a password'); | |
| return; | |
| } | |
| setLoading(true); | |
| setError(null); | |
| try { | |
| const response = await axios.post('/api/auth/verify', { password }); | |
| if (response.data.authenticated) { | |
| // Store auth state in sessionStorage (cleared when browser closes) | |
| sessionStorage.setItem('auth_verified', 'true'); | |
| onAuthenticated(); | |
| } | |
| } catch (err) { | |
| if (err.response?.status === 401) { | |
| setError('Invalid password. Please try again.'); | |
| } else { | |
| setError('Authentication failed. Please try again.'); | |
| } | |
| console.error('Auth error:', err); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="min-h-screen bg-gradient-to-br from-blue-600 to-purple-700 flex items-center justify-center p-4"> | |
| <motion.div | |
| initial={{ opacity: 0, scale: 0.9 }} | |
| animate={{ opacity: 1, scale: 1 }} | |
| className="bg-white rounded-2xl shadow-2xl p-8 w-full max-w-md" | |
| > | |
| <div className="text-center mb-8"> | |
| <div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4"> | |
| <FaLock className="text-3xl text-blue-600" /> | |
| </div> | |
| <h1 className="text-2xl font-bold text-gray-900">Certificate Manager</h1> | |
| <p className="text-gray-600 mt-2">Enter password to access</p> | |
| </div> | |
| <form onSubmit={handleSubmit} className="space-y-6"> | |
| <div> | |
| <label className="block text-sm font-medium text-gray-700 mb-2"> | |
| Password | |
| </label> | |
| <input | |
| type="password" | |
| value={password} | |
| onChange={(e) => setPassword(e.target.value)} | |
| className="w-full border border-gray-300 rounded-lg p-3 text-gray-900 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none" | |
| placeholder="Enter access password" | |
| autoFocus | |
| /> | |
| </div> | |
| {error && ( | |
| <motion.div | |
| initial={{ opacity: 0, y: -10 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm" | |
| > | |
| {error} | |
| </motion.div> | |
| )} | |
| <motion.button | |
| whileHover={{ scale: 1.02 }} | |
| whileTap={{ scale: 0.98 }} | |
| type="submit" | |
| disabled={loading} | |
| className={`w-full py-3 rounded-lg text-white font-semibold shadow-md flex items-center justify-center space-x-2 transition-all ${loading | |
| ? 'bg-gray-400 cursor-not-allowed' | |
| : 'bg-blue-600 hover:bg-blue-700 hover:shadow-lg' | |
| }`} | |
| > | |
| {loading ? ( | |
| <> | |
| <FaSpinner className="animate-spin" /> | |
| <span>Verifying...</span> | |
| </> | |
| ) : ( | |
| <> | |
| <FaLock /> | |
| <span>Access Application</span> | |
| </> | |
| )} | |
| </motion.button> | |
| </form> | |
| <p className="text-center text-xs text-gray-500 mt-6"> | |
| Protected application. Contact administrator for access. | |
| </p> | |
| </motion.div> | |
| </div> | |
| ); | |
| }; | |
| export default LoginScreen; | |