2026-02-17 23:33:06 +00:00

190 lines
8.5 KiB
TypeScript

import React, { useEffect } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { Formik, Form, Field } from 'formik';
import { mdiAccount, mdiAsterisk, mdiShieldCheck, mdiArrowLeft } from '@mdi/js';
import BaseButton from '../components/BaseButton';
import CardBox from '../components/CardBox';
import SectionFullScreen from '../components/SectionFullScreen';
import LayoutGuest from '../layouts/Guest';
import FormField from '../components/FormField';
import BaseIcon from '../components/BaseIcon';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
import { loginUser, resetAction, findMe } from '../stores/authSlice';
import { getPageTitle } from '../config';
const validate = (values: any) => {
const errors: any = {};
if (!values.email) {
errors.email = 'Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address';
}
if (!values.password) {
errors.password = 'Required';
}
return errors;
};
export default function LoginPage() {
const router = useRouter();
const dispatch = useAppDispatch();
const { isFetching, token } = useAppSelector((state) => state.auth);
const { action, businessId } = router.query;
const isClaiming = action === 'claim';
const title = 'Crafted Network'
useEffect(() => {
if (token) {
dispatch(findMe());
// If claiming, redirect back to business details after login
if (isClaiming && businessId) {
router.push(`/public/businesses-details?id=${businessId}`);
} else {
router.push('/dashboard');
}
}
}, [token, dispatch, isClaiming, businessId, router]);
useEffect(() => {
dispatch(resetAction());
}, [dispatch]);
const handleSubmit = async (values: any) => {
const { email, password } = values;
const rest = { email, password };
await dispatch(loginUser(rest));
};
return (
<SectionFullScreen bg="white">
<Head>
<title>{getPageTitle('Login')} | {title}</title>
</Head>
<div className="flex flex-col lg:flex-row min-h-screen w-full overflow-hidden">
{/* Left Side: Visual/Branding */}
<div className="hidden lg:flex lg:w-1/2 bg-slate-900 items-center justify-center p-12 relative overflow-hidden">
<div className="absolute top-0 left-0 w-full h-full opacity-20">
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-emerald-500 blur-[120px] rounded-full"></div>
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-blue-600 blur-[120px] rounded-full"></div>
</div>
<div className="relative z-10 text-center space-y-8 max-w-md">
<div className="inline-flex p-4 rounded-3xl bg-emerald-500 shadow-2xl shadow-emerald-500/20 rotate-3">
<BaseIcon path={mdiShieldCheck} size={48} className="text-slate-900" />
</div>
<h1 className="text-5xl font-black text-white tracking-tight">
The <span className="text-emerald-400">Crafted</span> Network
</h1>
<p className="text-xl text-slate-400 leading-relaxed font-medium">
Join the world&apos;s most trusted network for verified professional services.
</p>
</div>
</div>
{/* Right Side: Login Form */}
<div className="w-full lg:w-1/2 flex items-center justify-center p-6 md:p-12 bg-white">
<div className="w-full max-w-md space-y-10 animate-fade-in">
{/* Back Button */}
<Link href="/" className="inline-flex items-center text-sm font-bold text-slate-400 hover:text-emerald-600 transition-colors group">
<BaseIcon path={mdiArrowLeft} size={20} className="mr-2 group-hover:-translate-x-1 transition-transform" />
Back to Home
</Link>
<div className="space-y-4">
<h2 className="text-4xl font-black text-slate-900 tracking-tight">Welcome Back</h2>
{isClaiming ? (
<div className="bg-emerald-50 p-6 rounded-2xl border border-emerald-100 flex items-start space-x-4">
<div className="p-2 bg-emerald-100 rounded-xl text-emerald-600">
<BaseIcon path={mdiShieldCheck} size={24} />
</div>
<div className="space-y-1">
<h4 className="font-bold text-emerald-900">Verify Ownership</h4>
<p className="text-emerald-700 text-sm">Please login or create an account to verify ownership and take control of your business profile.</p>
</div>
</div>
) : (
<p className="text-slate-500">Log in to manage your professional presence and track your requests.</p>
)}
</div>
<CardBox className="border-none shadow-none p-0">
<Formik
initialValues={{ email: '', password: '' }}
validate={validate}
onSubmit={handleSubmit}
>
{({ errors, touched }) => (
<Form className="space-y-6">
<FormField label="Email Address" labelColor="text-slate-900 font-bold" help={touched.email && errors.email ? (errors.email as string) : "Your registered professional email"}>
<Field
name="email"
type="email"
placeholder="alex@example.com"
className={`w-full px-5 py-4 bg-slate-50 border-2 ${touched.email && errors.email ? 'border-red-500' : 'border-slate-100'} rounded-2xl focus:border-emerald-500 focus:ring-0 transition-all font-medium text-slate-900`}
/>
</FormField>
<FormField label="Password" labelColor="text-slate-900 font-bold" help={touched.password && errors.password ? (errors.password as string) : "Security first — keep it safe"}>
<Field
name="password"
type="password"
placeholder="••••••••"
className={`w-full px-5 py-4 bg-slate-50 border-2 ${touched.password && errors.password ? 'border-red-500' : 'border-slate-100'} rounded-2xl focus:border-emerald-500 focus:ring-0 transition-all font-medium text-slate-900`}
/>
</FormField>
<div className="flex items-center justify-between text-sm font-bold pt-2">
<Link href="/forgot-password" size="sm" className="text-emerald-600 hover:text-emerald-500 transition-colors">
Forgot Password?
</Link>
</div>
<BaseButton
type="submit"
color="emerald"
label={isFetching ? 'Verifying...' : 'Login to Dashboard'}
className="w-full py-5 rounded-2xl font-black text-lg shadow-xl shadow-emerald-500/20 active:scale-[0.98] transition-all"
disabled={isFetching}
/>
</Form>
)}
</Formik>
<div className="pt-10 text-center space-y-6">
<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-slate-100"></div>
</div>
<div className="relative flex justify-center text-xs uppercase font-bold tracking-widest text-slate-400">
<span className="bg-white px-4">New to the Network?</span>
</div>
</div>
<Link
href={isClaiming ? `/register?action=claim&businessId=${businessId}` : "/register"}
className="block w-full py-4 px-6 rounded-2xl bg-slate-50 border-2 border-slate-100 text-slate-900 font-bold hover:bg-slate-100 transition-all text-center"
>
Create Professional Account
</Link>
<p className='text-xs font-medium text-slate-400 pt-8'>
© 2026 <span>{title}</span>. All rights reserved. Professional Directory Platform.
</p>
</div>
</CardBox>
</div>
</div>
</div>
</SectionFullScreen>
);
}
LoginPage.getLayout = function getLayout(page: ReactElement) {
return page;
};