211 lines
7.1 KiB
TypeScript
211 lines
7.1 KiB
TypeScript
import React, { useState, useEffect, useRef, FormEvent, ChangeEvent } from 'react'
|
|
import { useDispatch } from 'react-redux';
|
|
import { useNavigate } from 'react-router-dom'
|
|
import { setUserSlice } from '../store/userSlice';
|
|
import DocVaLogoLight from '../../assets/logo-colored-vertical.svg'
|
|
import DocVaLogoDark from '../../assets/logo-white-vertical.svg'
|
|
import { useTheme } from '../helper/ThemeToggle';
|
|
import { Eye, EyeOff, KeyRound, Mail } from 'lucide-react';
|
|
|
|
const Login = () => {
|
|
const { theme, setTheme } = useTheme();
|
|
|
|
const inputRef = useRef<HTMLFormElement | null>(null)
|
|
const navigate = useNavigate()
|
|
const dispatch = useDispatch();
|
|
const [errorMessage, setErrorMessage] = useState("")
|
|
const [email, setEmail] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [isForgotMode, setIsForgotMode] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
|
|
useEffect(() => {
|
|
document.documentElement.setAttribute("data-theme", theme);
|
|
}, [theme]);
|
|
|
|
const logo = theme === "dark" ? DocVaLogoDark : DocVaLogoLight;
|
|
|
|
|
|
|
|
const handleLogin = async (e: FormEvent<HTMLFormElement> | KeyboardEvent) => {
|
|
e.preventDefault()
|
|
|
|
if (email === "" || password === "") return
|
|
setLoading(true);
|
|
try {
|
|
const response = await window.electron.login({ email, password });
|
|
console.log(response)
|
|
if (response.success) {
|
|
// setUser(response.user);
|
|
dispatch(setUserSlice({
|
|
id: response.user.id,
|
|
name: response.user.name,
|
|
email: response.user.email,
|
|
}));
|
|
navigate("/")
|
|
}
|
|
else if (response.success == false) {
|
|
console.log(response.error.message)
|
|
setErrorMessage(response.error.message)
|
|
}
|
|
} catch (error) {
|
|
console.log(error)
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
const handleForgotPassword = async (e: FormEvent<HTMLFormElement> | KeyboardEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
try {
|
|
const res = await window.electron.forgotPassword(email);
|
|
console.log("Forgot password response:", res);
|
|
alert("Password reset link sent to your email");
|
|
setIsForgotMode(false);
|
|
} catch (error) {
|
|
console.error("Forgot password error:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
// Detect Enter key press
|
|
useEffect(() => {
|
|
const handleEnter = (e: KeyboardEvent) => {
|
|
if (e.key === 'Enter') {
|
|
if (!isForgotMode) {
|
|
handleLogin(e)
|
|
} else {
|
|
handleForgotPassword(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
const inputElement = inputRef.current
|
|
inputElement?.addEventListener('keydown', handleEnter)
|
|
|
|
return () => inputElement?.removeEventListener('keydown', handleEnter)
|
|
}, [email, password])
|
|
|
|
return (
|
|
<>
|
|
<div className="min-h-screen flex items-center justify-center bg-linear-to-br from-primary/10 via-base-200 to-secondary/10 p-4">
|
|
<div className="card w-full max-w-md shadow-2xl bg-base-100 backdrop-blur-sm">
|
|
<div className="card-body">
|
|
<div className="flex justify-center items-center mb-4">
|
|
<div className="avatar placeholder">
|
|
{/* <div className="bg-primary text-primary-content rounded-full w-20 h-20 flex items-center justify-center"> */}
|
|
<img src={logo} className='w-35 h-23' />
|
|
{/* </div> */}
|
|
</div>
|
|
</div>
|
|
|
|
<h2 className="text-center text-3xl font-bold mb-2">Welcome Back</h2>
|
|
<p className="text-center text-base-content/60 mb-6">Sign in to Employee Tracker</p>
|
|
|
|
<form
|
|
ref={inputRef}
|
|
className="flex flex-col items-center justify-center w-full"
|
|
onSubmit={isForgotMode ? handleForgotPassword : handleLogin}
|
|
>
|
|
<div className="form-control w-90 px-5">
|
|
<label className="label">
|
|
<span className="label-text font-medium">Email Address</span>
|
|
</label>
|
|
<label className="input input-bordered flex items-center gap-2" tabIndex={0}>
|
|
{/* <Mail size={18} /> */}
|
|
<input
|
|
type="email"
|
|
name="email"
|
|
placeholder="you@example.com"
|
|
className="grow"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
required
|
|
/>
|
|
</label>
|
|
</div>
|
|
|
|
{!isForgotMode && (
|
|
<div className="form-control w-90 px-5 mt-3">
|
|
<label className="label">
|
|
<span className="label-text font-medium">Password</span>
|
|
</label>
|
|
|
|
<label className="input input-bordered flex items-center gap-2" tabIndex={0}>
|
|
{/* <KeyRound size={18} /> */}
|
|
|
|
<input
|
|
type={showPassword ? "text" : "password"}
|
|
name="password"
|
|
placeholder="Enter your password"
|
|
className="grow"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
required
|
|
/>
|
|
|
|
{/* Toggle Icon */}
|
|
<button
|
|
type="button"
|
|
className="cursor-pointer"
|
|
onClick={() => setShowPassword((prev) => !prev)}
|
|
>
|
|
{showPassword ? (
|
|
<EyeOff size={18} className="opacity-70" />
|
|
) : (
|
|
<Eye size={18} className="opacity-70" />
|
|
)}
|
|
</button>
|
|
</label>
|
|
</div>
|
|
)}
|
|
|
|
|
|
<div className="form-control w-90 px-5 mt-4">
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="btn btn-primary w-full"
|
|
>
|
|
{loading
|
|
? "Please wait..."
|
|
: isForgotMode
|
|
? "Send Reset Link"
|
|
: "Login"}
|
|
</button>
|
|
</div>
|
|
<div className="text-center mt-4">
|
|
{isForgotMode ? (
|
|
<button
|
|
type="button"
|
|
onClick={() => setIsForgotMode(false)}
|
|
className="link link-primary text-sm"
|
|
>
|
|
Back to Login
|
|
</button>
|
|
) : (
|
|
<button
|
|
type="button"
|
|
onClick={() => setIsForgotMode(true)}
|
|
className="link link-primary text-sm"
|
|
>
|
|
Forgot Password?
|
|
</button>
|
|
)}
|
|
</div>
|
|
{errorMessage && <span className='text-red-500 font-semibold mt-2'>{errorMessage}...!</span>}
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default Login
|