From ab3bf2038b2bb88ac96ce535e7c032365f7791c6 Mon Sep 17 00:00:00 2001 From: SanaullasAzaan Date: Wed, 24 Dec 2025 11:02:55 +0530 Subject: [PATCH] feat: improve responsiveness and refactor auth components - 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 --- ...-left-wave1 .svg => bottom-left-wave1.svg} | 0 src/components/auth/auth-button.tsx | 17 +- src/components/auth/auth-card.tsx | 12 +- src/components/auth/auth-form-card.tsx | 60 +++-- src/components/auth/auth-input.tsx | 4 +- src/components/auth/forgot-password-modal.tsx | 216 ++++++------------ src/components/auth/index.ts | 2 + src/components/auth/modal-icons.tsx | 32 +++ src/components/auth/otp-input.tsx | 139 +++++++++++ src/components/dashboard/agent-card.tsx | 6 +- src/components/dashboard/metric-card.tsx | 10 +- src/components/dashboard/sidebar.tsx | 50 ++-- src/components/layout/header.tsx | 26 +-- src/components/layout/root-layout.tsx | 2 +- src/pages/dashboard.tsx | 33 +-- src/pages/login-page.tsx | 95 +++++--- src/pages/sign-up.tsx | 20 +- 17 files changed, 444 insertions(+), 280 deletions(-) rename src/assets/images/backgrounds/{bottom-left-wave1 .svg => bottom-left-wave1.svg} (100%) create mode 100644 src/components/auth/modal-icons.tsx create mode 100644 src/components/auth/otp-input.tsx diff --git a/src/assets/images/backgrounds/bottom-left-wave1 .svg b/src/assets/images/backgrounds/bottom-left-wave1.svg similarity index 100% rename from src/assets/images/backgrounds/bottom-left-wave1 .svg rename to src/assets/images/backgrounds/bottom-left-wave1.svg diff --git a/src/components/auth/auth-button.tsx b/src/components/auth/auth-button.tsx index e1dc3c7..9fe3156 100644 --- a/src/components/auth/auth-button.tsx +++ b/src/components/auth/auth-button.tsx @@ -100,13 +100,13 @@ export function AuthButton({ disabled={disabled || isLoading} className={` flex items-center justify-center - h-[52px] - px-[18px] py-[13px] + min-h-[44px] h-11 md:h-[52px] + px-4 md:px-[18px] py-2.5 md:py-[13px] bg-[#00E2E0] border border-[rgba(255,255,255,0.2)] - rounded-[12px] + rounded-lg md:rounded-[12px] shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)] - text-[16px] font-semibold leading-6 text-black + text-sm md:text-base md:text-[16px] font-semibold leading-5 md:leading-6 text-black cursor-pointer hover:bg-[#00D4D2] active:bg-[#00C6C4] @@ -165,16 +165,17 @@ export function SocialButton({ diff --git a/src/components/auth/index.ts b/src/components/auth/index.ts index 0bda060..0958560 100644 --- a/src/components/auth/index.ts +++ b/src/components/auth/index.ts @@ -9,4 +9,6 @@ export { AuthInput } from './auth-input'; export { AuthButton, SocialButton, TextButton, GoogleIcon, AzureIcon } from './auth-button'; export { AuthFormCard } from './auth-form-card'; export { ForgotPasswordModal } from './forgot-password-modal'; +export { OTPInput } from './otp-input'; +export { CloseIcon, ArrowLeftIcon } from './modal-icons'; diff --git a/src/components/auth/modal-icons.tsx b/src/components/auth/modal-icons.tsx new file mode 100644 index 0000000..da32acf --- /dev/null +++ b/src/components/auth/modal-icons.tsx @@ -0,0 +1,32 @@ +/** + * Modal Icons Component + * @description Icon components for modal dialogs + * Follows AgenticIQ Enterprise Coding Guidelines v1.0 + */ + +/** + * Close icon component + * @description Renders an X icon for closing the modal + * @returns {JSX.Element} Close icon SVG + */ +export function CloseIcon(): JSX.Element { + return ( + + + + ); +} + +/** + * Left arrow icon component + * @description Renders a left-pointing arrow for back navigation + * @returns {JSX.Element} Arrow left icon SVG + */ +export function ArrowLeftIcon(): JSX.Element { + return ( + + + + ); +} + diff --git a/src/components/auth/otp-input.tsx b/src/components/auth/otp-input.tsx new file mode 100644 index 0000000..c72d41a --- /dev/null +++ b/src/components/auth/otp-input.tsx @@ -0,0 +1,139 @@ +/** + * OTP Input Component + * @description Six individual input boxes for OTP entry with auto-focus, + * backspace navigation, and paste support. + * Follows AgenticIQ Enterprise Coding Guidelines v1.0 + */ + +import { useRef, useEffect, type KeyboardEvent, type ClipboardEvent } from 'react'; +import { motion, type Easing } from 'framer-motion'; + +/** Animation timing constants */ +const ANIMATION_TIMING = { + OTP_AUTOFOCUS_DELAY: 300, + TRANSITION_DURATION: 0.25, + OTP_STAGGER_DELAY: 0.04, + OTP_LENGTH: 6, + OTP_MAX_INDEX: 5, +} as const; + +/** + * Props for OTPInput component + */ +interface OTPInputProps { + /** Current OTP values array */ + otp: string[]; + /** Setter function for OTP state */ + setOtp: React.Dispatch>; +} + +/** + * Smooth easing curve for animations + */ +const smoothEasing: Easing = [0.4, 0, 0.2, 1]; + +/** + * OTP Input component + * @description Six individual input boxes for OTP entry with auto-focus, + * backspace navigation, and paste support. + * @param {OTPInputProps} props - Component props + * @returns {JSX.Element} OTP input element + */ +export function OTPInput({ otp, setOtp }: OTPInputProps): JSX.Element { + const inputRefs = useRef<(HTMLInputElement | null)[]>([]); + + useEffect(() => { + const timer = setTimeout(() => { + inputRefs.current[0]?.focus(); + }, ANIMATION_TIMING.OTP_AUTOFOCUS_DELAY); + return () => clearTimeout(timer); + }, []); + + /** + * Handle input value change + * @param {number} index - Input index + * @param {string} value - New input value + */ + const handleChange = (index: number, value: string): void => { + if (value.length > 1) value = value.slice(-1); + if (value && !/^\d$/.test(value)) return; + + const newOtp = [...otp]; + newOtp[index] = value; + setOtp(newOtp); + + if (value && index < ANIMATION_TIMING.OTP_MAX_INDEX) { + inputRefs.current[index + 1]?.focus(); + } + }; + + /** + * Handle keyboard navigation + * @param {KeyboardEvent} event - Keyboard event + * @param {number} index - Current input index + */ + const handleKeyDown = (event: KeyboardEvent, index: number): void => { + if (event.key === 'Backspace' && !otp[index] && index > 0) { + inputRefs.current[index - 1]?.focus(); + } + }; + + /** + * Handle paste event for OTP auto-fill + * @param {ClipboardEvent} event - Clipboard event + */ + const handlePaste = (event: ClipboardEvent): void => { + event.preventDefault(); + const pastedData = event.clipboardData + .getData('text') + .replace(/\D/g, '') + .slice(0, ANIMATION_TIMING.OTP_LENGTH); + const newOtp = [...otp]; + for (let i = 0; i < pastedData.length; i++) { + newOtp[i] = pastedData[i]; + } + setOtp(newOtp); + const nextIndex = Math.min(pastedData.length, ANIMATION_TIMING.OTP_MAX_INDEX); + inputRefs.current[nextIndex]?.focus(); + }; + + return ( +
+ {otp.map((digit, index) => ( + { inputRefs.current[index] = el; }} + type="text" + inputMode="numeric" + maxLength={1} + value={digit} + onChange={(e) => handleChange(index, e.target.value)} + onKeyDown={(e) => handleKeyDown(e, index)} + onPaste={handlePaste} + className=" + w-10 h-10 md:w-[44px] md:h-[44px] + bg-[rgba(255,255,255,0.15)] + border border-[rgba(255,255,255,0.1)] + rounded-lg md:rounded-[8px] + text-center text-white text-base md:text-[18px] font-medium + outline-none + focus:border-[rgba(255,255,255,0.4)] + focus:bg-[rgba(255,255,255,0.2)] + transition-all duration-200 + flex-1 max-w-[44px] + " + placeholder="•" + aria-label={`Digit ${index + 1}`} + /> + ))} +
+ ); +} + diff --git a/src/components/dashboard/agent-card.tsx b/src/components/dashboard/agent-card.tsx index 43e3359..60fa098 100644 --- a/src/components/dashboard/agent-card.tsx +++ b/src/components/dashboard/agent-card.tsx @@ -27,15 +27,15 @@ export function AgentCard({ title, tags, description }: AgentCardProps) { ]; return ( -
-

{title}

+
+

{title}

{tags.map((tag, index) => ( 2 */ - className={`${tagColors[index] || tagColors[0]} px-3 py-1 text-xs font-medium text-gray-700 border border-gray-200`} + className={`${tagColors[index] || tagColors[0]} px-2 sm:px-3 py-1 text-xs font-medium text-gray-700 border border-gray-200 rounded`} > {tag} diff --git a/src/components/dashboard/metric-card.tsx b/src/components/dashboard/metric-card.tsx index d41fef9..3e4f9a8 100644 --- a/src/components/dashboard/metric-card.tsx +++ b/src/components/dashboard/metric-card.tsx @@ -51,17 +51,17 @@ export function MetricCard({ title, subtitle, value, gradient }: MetricCardProps //
// ); return ( -
-
+
+
- + {title} - + {subtitle}
-
+
{value}
diff --git a/src/components/dashboard/sidebar.tsx b/src/components/dashboard/sidebar.tsx index 0c61083..d85b2c2 100644 --- a/src/components/dashboard/sidebar.tsx +++ b/src/components/dashboard/sidebar.tsx @@ -12,47 +12,47 @@ import { Home, Settings } from 'lucide-react'; */ export function Sidebar() { return ( -
diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index 72ac848..475739d 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -51,37 +51,38 @@ export function Dashboard() { ]; return ( -
+
{/* Content Header: Welcome + Search/Create */} -
+
-

+

Welcome, Vijay! 👋

-

+

Monitor and manage your intelligent agents, knowledge bases, and automated workflows from a centralized dashboard

-
-
+
+
-
{/* Metrics Grid */} -
-
+
+
{/* Agents Section */} -
+
{/* Tabs */} -
+
{/* Agent Cards Grid */} -
+
{agents.map((agent, index) => ( { - console.log('Sign in:', { email, password }); + // TODO(AUTH-001): Implement actual sign in API call + void Promise.resolve({ email, password }); }; - const handleRegister = (data: { - firstName: string; - lastName: string; - email: string; - password: string; - }): void => { - console.log('Register:', data); + /** + * 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 => { - console.log('Google auth clicked'); + // TODO(AUTH-003): Implement Google OAuth flow }; + /** + * Handle Azure OAuth authentication + */ const handleAzureAuth = (): void => { - console.log('Azure auth clicked'); + // 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 => { - console.log('Password recovery requested for:', email); + // 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 => { - console.log('OTP verification:', otp); + // TODO(AUTH-006): Implement OTP verification API call + void Promise.resolve(otp); }; return ( @@ -68,11 +106,11 @@ export function LoginPage(): JSX.Element { {/* AgenticIQ Logo */} -
+
AgenticIQ Logo
@@ -80,12 +118,12 @@ export function LoginPage(): JSX.Element { {/* Main Content Grid */} -
+
{/* Left Column - Branding/Marketing Content */} {/* Right Column - Auth Form Card */} -
+
@@ -154,21 +193,20 @@ function BackgroundDecorations(): JSX.Element { /** * LeftColumnText component - * @description Left side text content (heading and description). + * @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 ( <>

Engineering the Future with Intelligent Agents

Deploy intelligent agents that automate complex workflows, enhance decision-making, and scale your operations. Built for enterprise reliability with seamless integration capabilities.

@@ -178,12 +216,13 @@ function LeftColumnText(): JSX.Element { /** * LeftColumn component - * @description Empty container for left side content. + * @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 ( -
+
{/* Text content is positioned absolutely on page level */}
); diff --git a/src/pages/sign-up.tsx b/src/pages/sign-up.tsx index 83605ba..af7e70f 100644 --- a/src/pages/sign-up.tsx +++ b/src/pages/sign-up.tsx @@ -7,12 +7,12 @@ import { Link } from '@tanstack/react-router'; export function SignUpPage() { return ( -
-
+
+
- AgenticIQ Logo -

Create your account

-

Join AgenticIQ to manage your agents and workflows.

+ AgenticIQ Logo +

Create your account

+

Join AgenticIQ to manage your agents and workflows.

@@ -20,7 +20,7 @@ export function SignUpPage() {
@@ -28,7 +28,7 @@ export function SignUpPage() {
@@ -36,19 +36,19 @@ export function SignUpPage() {
-

+

Already have an account?{' '} Sign in