From 4667c3bf003675a8fe3d64d2a912a0f329e2a2ac Mon Sep 17 00:00:00 2001 From: Ember <15190419+0xEmberZz@users.noreply.github.com> Date: Sun, 9 Nov 2025 00:36:28 +0800 Subject: [PATCH] feat(ui): add password strength validation and toggle visibility in registration and reset password forms (#773) Co-authored-by: tinkle-community --- web/package-lock.json | 10 ++ web/package.json | 1 + web/src/components/LoginPage.tsx | 44 +++---- web/src/components/RegisterPage.tsx | 141 ++++++++++++++++------- web/src/components/ResetPasswordPage.tsx | 77 ++++++++----- web/src/components/ui/input.tsx | 24 ++++ web/src/i18n/translations.ts | 32 +++++ web/src/lib/cn.ts | 7 ++ 8 files changed, 251 insertions(+), 85 deletions(-) create mode 100644 web/src/components/ui/input.tsx create mode 100644 web/src/lib/cn.ts diff --git a/web/package-lock.json b/web/package-lock.json index e1292d5d..9407dc46 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -16,6 +16,7 @@ "lucide-react": "^0.552.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-password-checklist": "^1.8.1", "recharts": "^2.15.2", "swr": "^2.2.5", "tailwind-merge": "^3.3.1", @@ -6982,6 +6983,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/react-password-checklist": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/react-password-checklist/-/react-password-checklist-1.8.1.tgz", + "integrity": "sha512-QHIU/OejxoH4/cIfYLHaHLb+yYc8mtL0Vr4HTmULxQg3ZNdI9Ni/yYf7pwLBgsUh4sseKCV/GzzYHWpHqejTGw==", + "license": "MIT", + "peerDependencies": { + "react": ">16.0.0-alpha || >17.0.0-alpha || >18.0.0-alpha" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", diff --git a/web/package.json b/web/package.json index 53c6f31e..0825e2c1 100644 --- a/web/package.json +++ b/web/package.json @@ -22,6 +22,7 @@ "lucide-react": "^0.552.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-password-checklist": "^1.8.1", "recharts": "^2.15.2", "swr": "^2.2.5", "tailwind-merge": "^3.3.1", diff --git a/web/src/components/LoginPage.tsx b/web/src/components/LoginPage.tsx index 1c2eae74..abf63959 100644 --- a/web/src/components/LoginPage.tsx +++ b/web/src/components/LoginPage.tsx @@ -3,6 +3,8 @@ import { useAuth } from '../contexts/AuthContext' import { useLanguage } from '../contexts/LanguageContext' import { t } from '../i18n/translations' import HeaderBar from './landing/HeaderBar' +import { Eye, EyeOff } from 'lucide-react' +import { Input } from './ui/input' export function LoginPage() { const { language } = useLanguage() @@ -10,6 +12,7 @@ export function LoginPage() { const [step, setStep] = useState<'login' | 'otp'>('login') const [email, setEmail] = useState('') const [password, setPassword] = useState('') + const [showPassword, setShowPassword] = useState(false) const [otpCode, setOtpCode] = useState('') const [userID, setUserID] = useState('') const [error, setError] = useState('') @@ -172,16 +175,10 @@ export function LoginPage() { > {t('email', language)} - setEmail(e.target.value)} - className="w-full px-3 py-2 rounded" - style={{ - background: 'var(--brand-black)', - border: '1px solid var(--panel-border)', - color: 'var(--brand-light-gray)', - }} placeholder={t('emailPlaceholder', language)} required /> @@ -194,19 +191,26 @@ export function LoginPage() { > {t('password', language)} - setPassword(e.target.value)} - className="w-full px-3 py-2 rounded" - style={{ - background: 'var(--brand-black)', - border: '1px solid var(--panel-border)', - color: 'var(--brand-light-gray)', - }} - placeholder={t('passwordPlaceholder', language)} - required - /> +
+ setPassword(e.target.value)} + className="pr-10" + placeholder={t('passwordPlaceholder', language)} + required + /> + +
+
@@ -193,18 +197,65 @@ export function RegisterPage() { > {t('confirmPassword', language)} - setConfirmPassword(e.target.value)} - className="w-full px-3 py-2 rounded" - style={{ - background: 'var(--brand-black)', - border: '1px solid var(--panel-border)', - color: 'var(--brand-light-gray)', +
+ setConfirmPassword(e.target.value)} + className="pr-10" + placeholder={t('confirmPasswordPlaceholder', language)} + required + /> + +
+
+ + {/* 密码规则清单(通过才允许提交) */} +
+
+ {t('passwordRequirements', language)} +
+ setPasswordValid(isValid)} />
@@ -256,7 +307,9 @@ export function RegisterPage() {