2026-01-25 14:15:14 +00:00

272 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
import BaseButton from '../components/BaseButton';
import CardBox from '../components/CardBox';
import BaseIcon from "../components/BaseIcon";
import { mdiInformation, mdiEye, mdiEyeOff, mdiAccountCircle } from '@mdi/js';
import SectionFullScreen from '../components/SectionFullScreen';
import LayoutGuest from '../layouts/Guest';
import { Field, Form, Formik } from 'formik';
import FormField from '../components/FormField';
import FormCheckRadio from '../components/FormCheckRadio';
import BaseDivider from '../components/BaseDivider';
import BaseButtons from '../components/BaseButtons';
import { useRouter } from 'next/router';
import { getPageTitle } from '../config';
import { findMe, loginUser, resetAction } from '../stores/authSlice';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
import Link from 'next/link';
import {toast, ToastContainer} from "react-toastify";
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'
import Logo from '../components/Logo';
export default function Login() {
const router = useRouter();
const dispatch = useAppDispatch();
const textColor = useAppSelector((state) => state.style.linkColor);
const iconsColor = useAppSelector((state) => state.style.iconsColor);
const notify = (type, msg) => toast(msg, { type });
const [ illustrationImage, setIllustrationImage ] = useState({
src: undefined,
photographer: undefined,
photographer_url: undefined,
})
const [ illustrationVideo, setIllustrationVideo ] = useState({video_files: []})
const [contentType, setContentType] = useState('video');
const [contentPosition, setContentPosition] = useState('right');
const [showPassword, setShowPassword] = useState(false);
const { currentUser, isFetching, errorMessage, token, notify:notifyState } = useAppSelector(
(state) => state.auth,
);
const [initialValues, setInitialValues] = React.useState({ email:'admin@flatlogic.com',
password: '4b8a182c',
remember: true })
const title = 'ADML EXCHANGE'
// Fetch Pexels image/video
useEffect( () => {
async function fetchData() {
const image = await getPexelsImage()
const video = await getPexelsVideo()
setIllustrationImage(image);
setIllustrationVideo(video);
}
fetchData();
}, []);
// Fetch user data
useEffect(() => {
if (token) {
dispatch(findMe());
}
}, [token, dispatch]);
// Redirect to dashboard if user is logged in
useEffect(() => {
if (currentUser?.id) {
router.push('/dashboard');
}
}, [currentUser?.id, router]);
// Show error message if there is one
useEffect(() => {
if (errorMessage){
notify('error', errorMessage)
}
}, [errorMessage])
// Show notification if there is one
useEffect(() => {
if (notifyState?.showNotification) {
notify('success', notifyState?.textNotification)
dispatch(resetAction());
}
}, [notifyState?.showNotification])
const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};
const handleSubmit = async (value) => {
const {remember, ...rest} = value
await dispatch(loginUser(rest));
};
const handleDemoLogin = async () => {
const demoValues = {
email: 'admin@flatlogic.com',
password: '4b8a182c'
};
setInitialValues({ ...demoValues, remember: true });
await dispatch(loginUser(demoValues));
};
const setLogin = (target: HTMLElement) => {
setInitialValues(prev => ({
...prev,
email : target.innerText.trim(),
password: target.dataset.password ?? '',
}));
};
const imageBlock = (image) => (
<div className="hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3"
style={{
backgroundImage: `${image ? `url(${image.src?.original})` : 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'}`,
backgroundSize: 'cover',
backgroundPosition: 'left center',
backgroundRepeat: 'no-repeat',
}}>
<div className="flex justify-center w-full bg-blue-300/20">
<a className="text-[8px]" href={image?.photographer_url} target="_blank" rel="noreferrer">Photo
by {image?.photographer} on Pexels</a>
</div>
</div>
)
const videoBlock = (video) => {
if (video?.video_files?.length > 0) {
return (
<div className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'>
<video
className='absolute top-0 left-0 w-full h-full object-cover'
autoPlay
loop
muted
>
<source src={video.video_files[0]?.link} type='video/mp4'/>
Your browser does not support the video tag.
</video>
<div className='flex justify-center w-full bg-blue-300/20 z-10'>
<a
className='text-[8px]'
href={video.user.url}
target='_blank'
rel='noreferrer'
>
Video by {video.user.name} on Pexels
</a>
</div>
</div>)
}
};
return (
<div style={contentPosition === 'background' ? {
backgroundImage: `${
illustrationImage
? `url(${illustrationImage.src?.original})`
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
}`,
backgroundSize: 'cover',
backgroundPosition: 'left center',
backgroundRepeat: 'no-repeat',
} : {}}>
<Head>
<title>{getPageTitle('Login')}</title>
</Head>
<SectionFullScreen bg='violet'>
<div className={`flex ${contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'} min-h-screen w-full bg-black/40 backdrop-blur-sm`}>
{contentType === 'image' && contentPosition !== 'background' ? imageBlock(illustrationImage) : null}
{contentType === 'video' && contentPosition !== 'background' ? videoBlock(illustrationVideo) : null}
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
<CardBox className='w-full md:w-3/5 lg:w-1/2 shadow-2xl bg-white/90 dark:bg-dark-900/90'>
<div className="flex flex-col items-center mb-8">
<Logo className="text-3xl mb-4" />
<h2 className="text-2xl font-black tracking-tight uppercase">Welcome to ADML</h2>
<p className="text-gray-500 text-sm font-medium">Please sign in to continue trading</p>
</div>
<Formik
initialValues={initialValues}
enableReinitialize
onSubmit={(values) => handleSubmit(values)}
>
<Form className="space-y-4">
<FormField
label='Email / Account'
help='Enter your registered email'>
<Field name='email' placeholder="example@adml.com" />
</FormField>
<div className='relative'>
<FormField
label='Password'
help='Enter your password'>
<Field name='password' type={showPassword ? 'text' : 'password'} placeholder="••••••••" />
</FormField>
<div
className='absolute bottom-8 right-0 pr-3 flex items-center cursor-pointer'
onClick={togglePasswordVisibility}
>
<BaseIcon
className='text-gray-500 hover:text-gray-700'
size={20}
path={showPassword ? mdiEyeOff : mdiEye}
/>
</div>
</div>
<div className={'flex justify-between items-center px-1'}>
<FormCheckRadio type='checkbox' label='Stay logged in'>
<Field type='checkbox' name='remember' />
</FormCheckRadio>
<Link className={`${textColor} text-sm font-bold`} href={'/forgot'}>
Forgot password?
</Link>
</div>
<BaseDivider />
<BaseButtons vertical className="space-y-3">
<BaseButton
className={'w-full py-3 font-black text-lg'}
type='submit'
label={isFetching ? 'Verifying...' : 'Sign In'}
color='info'
disabled={isFetching}
/>
<BaseButton
className={'w-full py-3 font-bold border-2 border-blue-600/50 hover:bg-blue-600 hover:text-white transition-all'}
type='button'
onClick={handleDemoLogin}
label='Demo Account Login'
outline
color='info'
icon={mdiAccountCircle}
/>
</BaseButtons>
<p className={'text-center mt-6 text-sm font-medium text-gray-500'}>
Dont have an account yet?{' '}
<Link className={`${textColor} font-black underline decoration-2 underline-offset-4`} href={'/register'}>
Create Account
</Link>
</p>
</Form>
</Formik>
</CardBox>
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-4 border border-white/10 text-[10px] text-gray-400 font-bold uppercase tracking-widest text-center max-w-md">
Warning: Please verify the URL is adml-exchange.com to prevent phishing.
</div>
</div>
</div>
</SectionFullScreen>
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row py-8 border-t border-white/5'>
<p className='text-sm font-bold opacity-60 uppercase tracking-widest'>© 2026 ADML EXCHANGE. ALL RIGHTS RESERVED.</p>
<div className="flex space-x-6 md:ml-12 mt-4 md:mt-0">
<Link className='text-xs font-black uppercase tracking-widest hover:text-blue-500' href='/privacy-policy/'>Privacy Policy</Link>
<Link className='text-xs font-black uppercase tracking-widest hover:text-blue-500' href='/terms/'>Terms of Service</Link>
</div>
</div>
<ToastContainer />
</div>
);
}
Login.getLayout = function getLayout(page: ReactElement) {
return <LayoutGuest>{page}</LayoutGuest>;
};