- Fix responsiveness at md (768px) and lg (1024px) breakpoints across all pages - Reduce forgot-password-modal.tsx from 472 to 319 lines by extracting components - Extract OTPInput and modal icons into separate files for better code organization - Optimize card sizes and text scaling for lg (1024px) breakpoint - Adjust login page layout: smaller card and reduced text sizes at lg breakpoint - Improve responsive typography, spacing, and touch targets across all components - Update all pages (login, sign-up, dashboard) with proper mobile-first responsive design
230 lines
8.4 KiB
TypeScript
230 lines
8.4 KiB
TypeScript
/**
|
|
* Login Page Component
|
|
* @description Main authentication page with 2-column responsive layout.
|
|
* Left column displays branding/marketing content, right column contains auth form.
|
|
* Supports both Sign In and Register modes with animated transitions.
|
|
* Includes Forgot Password modal with OTP verification.
|
|
* Follows AgenticIQ Enterprise Coding Guidelines v1.0
|
|
*/
|
|
|
|
import { useState } from 'react';
|
|
import agenticiqLogo from '@/assets/images/logo/AgenticIQLogo.svg';
|
|
import topRightGlow from '@/assets/images/backgrounds/top-right-glow.svg';
|
|
import bottomLeftWave1 from '@/assets/images/backgrounds/bottom-left-wave1.svg';
|
|
import bottomLeftWave2 from '@/assets/images/backgrounds/bottom-left-wave2.svg';
|
|
import logoGlowBg from '@/assets/images/backgrounds/logo-glow-bg.svg';
|
|
import { AuthFormCard, ForgotPasswordModal } from '@/components/auth';
|
|
|
|
/**
|
|
* Register form data interface
|
|
*/
|
|
interface RegisterFormData {
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
password: string;
|
|
}
|
|
|
|
/**
|
|
* LoginPage component
|
|
* @description Full-page login layout with responsive 2-column grid structure.
|
|
* Mobile-first design that stacks columns on smaller screens.
|
|
* @returns {JSX.Element} LoginPage element
|
|
*/
|
|
export function LoginPage(): JSX.Element {
|
|
const [showForgotPwd, setShowForgotPwd] = useState(false);
|
|
|
|
/**
|
|
* Handle sign in form submission
|
|
* @param {string} email - User email address
|
|
* @param {string} password - User password
|
|
*/
|
|
const handleSignIn = (email: string, password: string): void => {
|
|
// TODO(AUTH-001): Implement actual sign in API call
|
|
void Promise.resolve({ email, password });
|
|
};
|
|
|
|
/**
|
|
* Handle register form submission
|
|
* @param {RegisterFormData} data - Registration form data
|
|
*/
|
|
const handleRegister = (data: RegisterFormData): void => {
|
|
// TODO(AUTH-002): Implement actual registration API call
|
|
void Promise.resolve(data);
|
|
};
|
|
|
|
/**
|
|
* Handle Google OAuth authentication
|
|
*/
|
|
const handleGoogleAuth = (): void => {
|
|
// TODO(AUTH-003): Implement Google OAuth flow
|
|
};
|
|
|
|
/**
|
|
* Handle Azure OAuth authentication
|
|
*/
|
|
const handleAzureAuth = (): void => {
|
|
// TODO(AUTH-004): Implement Azure OAuth flow
|
|
};
|
|
|
|
/**
|
|
* Open forgot password modal
|
|
*/
|
|
const handleForgotPasswordClick = (): void => {
|
|
setShowForgotPwd(true);
|
|
};
|
|
|
|
/**
|
|
* Close forgot password modal
|
|
*/
|
|
const handleForgotPasswordClose = (): void => {
|
|
setShowForgotPwd(false);
|
|
};
|
|
|
|
/**
|
|
* Handle forgot password email submission
|
|
* @param {string} email - Email for password recovery
|
|
*/
|
|
const handleForgotPasswordSubmit = (email: string): void => {
|
|
// TODO(AUTH-005): Implement password recovery API call
|
|
void Promise.resolve(email);
|
|
};
|
|
|
|
/**
|
|
* Handle OTP verification submission
|
|
* @param {string} otp - 6-digit OTP code
|
|
*/
|
|
const handleOtpVerify = (otp: string): void => {
|
|
// TODO(AUTH-006): Implement OTP verification API call
|
|
void Promise.resolve(otp);
|
|
};
|
|
|
|
return (
|
|
<div className="login-page">
|
|
<div className="relative min-h-screen w-full overflow-hidden bg-white">
|
|
{/* Decorative Background Elements */}
|
|
<BackgroundDecorations />
|
|
|
|
{/* AgenticIQ Logo */}
|
|
<div className="absolute left-4 top-4 md:left-8 lg:left-12 xl:left-[80px] md:top-8 lg:top-8 xl:top-[54px] z-20">
|
|
<img
|
|
src={agenticiqLogo}
|
|
alt="AgenticIQ Logo"
|
|
className="h-16 w-auto md:h-24 lg:h-20 xl:h-[117px]"
|
|
/>
|
|
</div>
|
|
|
|
{/* Left Side Text Content */}
|
|
<LeftColumnText />
|
|
|
|
{/* Main Content Grid */}
|
|
<div className="relative z-10 grid min-h-screen grid-cols-1 md:grid-cols-1 lg:grid-cols-[1fr_480px] xl:grid-cols-[1fr_600px] 2xl:grid-cols-[1fr_620px]">
|
|
{/* Left Column - Branding/Marketing Content */}
|
|
<LeftColumn />
|
|
|
|
{/* Right Column - Auth Form Card */}
|
|
<div className="flex items-center justify-center md:justify-center lg:justify-start px-4 py-8 md:px-6 md:py-12 lg:px-4 lg:py-12 xl:px-0 xl:py-16">
|
|
<AuthFormCard
|
|
initialMode="signin"
|
|
onSignIn={handleSignIn}
|
|
onRegister={handleRegister}
|
|
onGoogleAuth={handleGoogleAuth}
|
|
onAzureAuth={handleAzureAuth}
|
|
onForgotPassword={handleForgotPasswordClick}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Forgot Password Modal */}
|
|
<ForgotPasswordModal
|
|
isOpen={showForgotPwd}
|
|
onClose={handleForgotPasswordClose}
|
|
onSubmit={handleForgotPasswordSubmit}
|
|
onVerify={handleOtpVerify}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* BackgroundDecorations component
|
|
* @description Renders decorative background images matching Figma design.
|
|
* All images are purely decorative and hidden from screen readers.
|
|
* @returns {JSX.Element} Background decoration elements
|
|
*/
|
|
function BackgroundDecorations(): JSX.Element {
|
|
return (
|
|
<div className="pointer-events-none absolute inset-0 z-0" aria-hidden="true">
|
|
{/* Logo glow background */}
|
|
<img
|
|
src={logoGlowBg}
|
|
alt=""
|
|
className="absolute left-0 top-0 h-auto w-auto opacity-[0.08] hidden md:block"
|
|
style={{ width: '550px', height: 'auto', maxHeight: '350px' }}
|
|
/>
|
|
|
|
{/* Top-right glow decoration */}
|
|
<img
|
|
src={topRightGlow}
|
|
alt=""
|
|
className="absolute right-0 top-0 h-auto w-auto opacity-[0.15] hidden md:block"
|
|
style={{ width: '515px', height: 'auto', maxHeight: '614px' }}
|
|
/>
|
|
|
|
{/* Bottom-left wave decoration 1 */}
|
|
<img
|
|
src={bottomLeftWave1}
|
|
alt=""
|
|
className="absolute bottom-0 left-0 h-auto w-auto opacity-[0.15] hidden md:block"
|
|
style={{ width: '559px', height: 'auto', maxHeight: '613px' }}
|
|
/>
|
|
|
|
{/* Bottom-left wave decoration 2 */}
|
|
<img
|
|
src={bottomLeftWave2}
|
|
alt=""
|
|
className="absolute bottom-0 left-0 h-auto w-auto opacity-[0.15] hidden md:block"
|
|
style={{ width: '350px', height: 'auto', maxHeight: '500px' }}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* LeftColumnText component
|
|
* @description Left side text content with heading and description.
|
|
* Positioned absolutely to match Figma design specifications.
|
|
* @returns {JSX.Element} Text elements positioned as per Figma
|
|
*/
|
|
function LeftColumnText(): JSX.Element {
|
|
return (
|
|
<>
|
|
<h1
|
|
className="hidden lg:block absolute text-2xl md:text-3xl lg:text-2xl xl:text-[36px] font-semibold text-black leading-tight lg:leading-tight xl:leading-normal whitespace-pre-wrap z-20 lg:left-12 lg:top-64 lg:w-80 xl:left-[110px] xl:top-[348px] xl:w-[513px]"
|
|
>
|
|
Engineering the Future with Intelligent Agents
|
|
</h1>
|
|
<p
|
|
className="hidden lg:block absolute text-sm md:text-base lg:text-sm xl:text-[24px] font-normal text-[rgba(0,0,0,0.75)] leading-relaxed lg:leading-relaxed xl:leading-normal whitespace-pre-wrap z-20 lg:left-12 lg:top-96 lg:w-80 xl:left-[110px] xl:top-[482px] xl:w-[607px]"
|
|
>
|
|
Deploy intelligent agents that automate complex workflows, enhance decision-making, and scale your operations. Built for enterprise reliability with seamless integration capabilities.
|
|
</p>
|
|
</>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* LeftColumn component
|
|
* @description Empty container for left side content in the grid layout.
|
|
* Text content is positioned absolutely on the page level for precise control.
|
|
* @returns {JSX.Element} Left column container
|
|
*/
|
|
function LeftColumn(): JSX.Element {
|
|
return (
|
|
<div className="hidden md:hidden lg:flex flex-col justify-center px-6 py-12 md:px-12 lg:px-8 xl:px-20 2xl:px-28">
|
|
{/* Text content is positioned absolutely on page level */}
|
|
</div>
|
|
);
|
|
}
|