From db4e5540ad14adc28436726446a2fd89ddbf8cc4 Mon Sep 17 00:00:00 2001 From: YANG JIANKUAN Date: Fri, 3 Apr 2026 13:18:05 +0800 Subject: [PATCH] feat: redesign login page with left-right split layout and OAuth buttons --- packages/web/src/pages/Login.tsx | 172 ++++++++++++++++++------------- 1 file changed, 99 insertions(+), 73 deletions(-) diff --git a/packages/web/src/pages/Login.tsx b/packages/web/src/pages/Login.tsx index 86e99ea..e8e23ef 100644 --- a/packages/web/src/pages/Login.tsx +++ b/packages/web/src/pages/Login.tsx @@ -1,6 +1,9 @@ import { useState } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { useAuth } from '../lib/auth'; +import { useI18n } from '../lib/i18n'; +import AuthBranding from '../components/AuthBranding'; +import OAuthButtons from '../components/OAuthButtons'; export default function Login() { const [email, setEmail] = useState(''); @@ -11,7 +14,8 @@ export default function Login() { const { login } = useAuth(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); - const redirectTo = searchParams.get('redirect') || '/'; + const redirectTo = searchParams.get('redirect') || '/dashboard'; + const { t } = useI18n(); const validate = () => { const errors: { email?: string; password?: string } = {}; @@ -43,82 +47,104 @@ export default function Login() { }; return ( -
- {/* Subtle grid background */} -
- {/* Radial fade */} -
+
+ {/* Left panel — branding (hidden on mobile) */} + -
- {/* Brand */} -
-
- - - + {/* Right panel — form */} +
+ {/* Subtle grid background */} +
+
+ +
+ {/* Mobile-only brand (visible when left panel is hidden) */} +
+
+ + + + + +
+

{t('auth.productName')}

+

{t('auth.slogan')}

-

Sign in to AgentFox

-

API documentation for LLMs

-
- {/* Card */} -
- {error && ( -
- - {error} -
- )} -
-
- - { setEmail(e.target.value); if (fieldErrors.email) setFieldErrors(prev => ({ ...prev, email: undefined })); }} - className={`input-base ${fieldErrors.email ? 'border-danger! focus:border-danger! focus:shadow-[0_0_0_3px_var(--danger-muted)]!' : ''}`} - placeholder="you@example.com" - /> - {fieldErrors.email && ( -

- - {fieldErrors.email} -

- )} -
-
- - { setPassword(e.target.value); if (fieldErrors.password) setFieldErrors(prev => ({ ...prev, password: undefined })); }} - className={`input-base ${fieldErrors.password ? 'border-danger! focus:border-danger! focus:shadow-[0_0_0_3px_var(--danger-muted)]!' : ''}`} - placeholder="Enter your password" - /> - {fieldErrors.password && ( -

- - {fieldErrors.password} -

- )} -
- -
-
+ {/* Title (desktop) */} +
+

{t('auth.login.title')}

+
-

- Don't have an account?{' '} - Sign Up -

+ {/* Card */} +
+ {error && ( +
+ + {error} +
+ )} +
+
+ + { setEmail(e.target.value); if (fieldErrors.email) setFieldErrors(prev => ({ ...prev, email: undefined })); }} + className={`input-base ${fieldErrors.email ? 'border-danger! focus:border-danger! focus:shadow-[0_0_0_3px_var(--danger-muted)]!' : ''}`} + placeholder="you@example.com" + /> + {fieldErrors.email && ( +

+ + {fieldErrors.email} +

+ )} +
+
+ + { setPassword(e.target.value); if (fieldErrors.password) setFieldErrors(prev => ({ ...prev, password: undefined })); }} + className={`input-base ${fieldErrors.password ? 'border-danger! focus:border-danger! focus:shadow-[0_0_0_3px_var(--danger-muted)]!' : ''}`} + placeholder="Enter your password" + /> + {fieldErrors.password && ( +

+ + {fieldErrors.password} +

+ )} +
+ +
+ + {/* Divider */} +
+
+ {t('auth.login.or')} +
+
+ + {/* OAuth buttons */} + +
+ +

+ {t('auth.login.noAccount')}{' '} + {t('auth.login.signUp')} +

+
);