mirror of
https://github.com/willchen96/mike.git
synced 2026-06-26 21:39:39 +02:00
- Add SecurityPage component for managing MFA settings, including enrollment and verification. - Create MfaLoginGate to handle MFA verification state during login. - Develop MfaVerificationPopup for user input of verification codes. - Implement VerifyMfaPage for the MFA verification process after login. - Introduce reusable VerificationCodeInput component for entering verification codes. - Integrate Supabase MFA API for managing factors and verification. - Add loading states and error handling for a better user experience.
137 lines
5.8 KiB
TypeScript
137 lines
5.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { supabase } from "@/lib/supabase";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import Link from "next/link";
|
|
import { SiteLogo } from "@/components/site-logo";
|
|
import { useAuth } from "@/contexts/AuthContext";
|
|
|
|
const authGlassCardClassName =
|
|
"rounded-2xl border border-white/70 bg-white/72 p-8 shadow-[0_4px_14px_rgba(15,23,42,0.045),inset_0_1px_0_rgba(255,255,255,0.86),inset_0_-8px_18px_rgba(255,255,255,0.12)] backdrop-blur-2xl";
|
|
const authInputClassName =
|
|
"rounded-lg border border-transparent bg-gray-100 px-3 shadow-none focus-visible:border-gray-200 focus-visible:ring-2 focus-visible:ring-gray-300/45";
|
|
|
|
export default function LoginPage() {
|
|
const router = useRouter();
|
|
const { isAuthenticated, authLoading } = useAuth();
|
|
const [email, setEmail] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!authLoading && isAuthenticated) {
|
|
router.replace("/assistant");
|
|
}
|
|
}, [authLoading, isAuthenticated, router]);
|
|
|
|
const handleLogin = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const { data, error } = await supabase.auth.signInWithPassword({
|
|
email,
|
|
password,
|
|
});
|
|
|
|
if (error) throw error;
|
|
|
|
router.push("/assistant");
|
|
} catch (error: any) {
|
|
setError(error.message || "An error occurred during login");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-dvh bg-gray-50/80 flex items-start justify-center px-6 pt-32 md:pt-40 pb-10 relative">
|
|
<div className="absolute top-4 md:top-8 left-1/2 -translate-x-1/2">
|
|
<SiteLogo size="lg" asLink />
|
|
</div>
|
|
<div className="w-full max-w-md">
|
|
{/* Login Form */}
|
|
<div className={`${authGlassCardClassName} mb-4`}>
|
|
<div className="flex justify-between items-center mb-6">
|
|
<h2 className="text-left text-2xl font-serif">
|
|
Log In
|
|
</h2>
|
|
<div className="bg-gray-200/70 p-1 rounded-lg flex text-xs font-medium shadow-[inset_0_1px_0_rgba(255,255,255,0.65),inset_0_-3px_8px_rgba(148,163,184,0.16)] backdrop-blur-xl">
|
|
<span className="text-gray-700 px-3 py-1 bg-white/85 rounded-md shadow-[0_1px_4px_rgba(15,23,42,0.06)]">
|
|
Log in
|
|
</span>
|
|
<Link
|
|
href="/signup"
|
|
className="px-3 py-1 text-gray-500 hover:text-gray-900"
|
|
>
|
|
Sign up
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
<form onSubmit={handleLogin} className="space-y-4">
|
|
<div>
|
|
<label
|
|
htmlFor="email"
|
|
className="block text-sm font-medium text-gray-700 mb-2"
|
|
>
|
|
Email
|
|
</label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
placeholder="Enter your email"
|
|
required
|
|
className={`w-full ${authInputClassName}`}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label
|
|
htmlFor="password"
|
|
className="block text-sm font-medium text-gray-700 mb-2"
|
|
>
|
|
Password
|
|
</label>
|
|
<Input
|
|
id="password"
|
|
type="password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
placeholder="Enter your password"
|
|
required
|
|
className={`w-full ${authInputClassName}`}
|
|
/>
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="text-red-600 text-sm bg-red-50 p-3 rounded">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<Button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="w-full mt-5 bg-black hover:bg-gray-900 text-white"
|
|
>
|
|
{loading ? "Logging in..." : "Log in"}
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
<p className="text-center text-xs text-gray-500 leading-relaxed px-2">
|
|
Mike hosted on MikeOSS.com is currently a demo service.
|
|
Please do not upload, submit, or store sensitive,
|
|
confidential, privileged, client, or personally
|
|
identifiable documents.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|