Aditya DN
Upload 6 files
5372a29 verified
import React, { useState, useEffect, useRef } from 'react';
import { Mode } from '@/types';
interface LoginModalProps {
onLoginSuccess: (mode: Mode) => void;
onClose: () => void;
}
const LoginModal: React.FC<LoginModalProps> = ({ onLoginSuccess, onClose }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [hasFocused, setHasFocused] = useState(false);
const [error, setError] = useState<string | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const modalRef = useRef<HTMLDivElement>(null);
const usernameRef = useRef<HTMLInputElement>(null);
const CONTROLLER_NAME = process.env.CONTROLLER_NAME || '';
const CONTROLLER_PASSWORD = process.env.CONTROLLER_PASSWORD || '';
if (!CONTROLLER_NAME || !CONTROLLER_PASSWORD) {
throw new Error("Environment variables for login credentials are not set.");
}
useEffect(() => {
if (!hasFocused) {
setHasFocused(true);
usernameRef.current?.focus();
}
const handleEsc = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onClose();
}
};
window.addEventListener('keydown', handleEsc);
return () => {
window.removeEventListener('keydown', handleEsc);
};
}, [onClose]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setError(null);
setIsSubmitting(true);
setTimeout(() => {
if (username === CONTROLLER_NAME && password === CONTROLLER_PASSWORD) {
onLoginSuccess('controller');
} else {
setError('Username atau PIN/Password salah. Silakan coba lagi.');
}
setIsSubmitting(false);
}, 500);
};
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
if (modalRef.current && e.target === modalRef.current) {
onClose();
}
};
return (
<div
ref={modalRef}
onClick={handleBackdropClick}
className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 transition-opacity duration-300 animate-fade-in"
>
<div className="bg-gray-800 border border-gray-700 rounded-lg shadow-xl p-6 sm:p-8 w-full max-w-sm transform animate-scale-in">
<h2 className="text-2xl font-bold text-center text-indigo-400 mb-6">Login</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="username" className="block text-sm font-medium text-gray-300 mb-1">
Username
</label>
<input
ref={usernameRef}
type="text"
id="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="w-full bg-gray-900 border border-gray-600 rounded-md px-3 py-2 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
required
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-300 mb-1">
PIN / Password
</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full bg-gray-900 border border-gray-600 rounded-md px-3 py-2 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
required
/>
</div>
{error && <p className="text-red-400 text-sm text-center">{error}</p>}
<div className="pt-2 flex flex-col sm:flex-row-reverse gap-3">
<button
type="submit"
disabled={isSubmitting}
className="w-full px-4 py-2 bg-indigo-600 hover:bg-indigo-700 rounded-md font-semibold text-white transition-colors disabled:bg-indigo-800 disabled:cursor-not-allowed flex items-center justify-center"
>
{isSubmitting && <SpinnerIcon />}
{isSubmitting ? 'Memverifikasi...' : 'Login'}
</button>
<button
type="button"
onClick={onClose}
className="w-full px-4 py-2 bg-gray-600 hover:bg-gray-700 rounded-md font-semibold text-white transition-colors"
>
Batal
</button>
</div>
</form>
</div>
<style>{`
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
.animate-fade-in { animation: fade-in 0.3s ease-out forwards; }
.animate-scale-in { animation: scale-in 0.3s ease-out forwards; }
`}</style>
</div>
);
};
const SpinnerIcon = () => (
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" 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>
);
export default LoginModal;