diff --git a/apps/frontend/app/[locale]/admin/login/page.tsx b/apps/frontend/app/[locale]/admin/login/page.tsx index befdf59..9392a3e 100644 --- a/apps/frontend/app/[locale]/admin/login/page.tsx +++ b/apps/frontend/app/[locale]/admin/login/page.tsx @@ -4,17 +4,19 @@ import { useState } from 'react'; import { useTranslations } from 'next-intl'; import { Link, useRouter } from '@/i18n/navigation'; import Image from 'next/image'; -import { ShieldCheck } from 'lucide-react'; +import { ShieldCheck, Eye, EyeOff } from 'lucide-react'; import { login as apiLogin, getCurrentUser, logout as apiLogout } from '@/lib/api/auth'; import { useAuth } from '@/lib/context/auth-context'; export default function AdminLoginPage() { const t = useTranslations('admin.login'); + const tCommon = useTranslations('common'); const router = useRouter(); const { refreshUser } = useAuth(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); @@ -94,17 +96,28 @@ export default function AdminLoginPage() { {t('passwordLabel')} - setPassword(e.target.value)} - className="input w-full" - placeholder={t('passwordPlaceholder')} - autoComplete="current-password" - disabled={isLoading} - required - /> +
{passwordForm.formState.errors.currentPassword.message} @@ -347,13 +369,24 @@ export default function ProfilePage() { > {t('passwordForm.new')} - +
{passwordForm.formState.errors.newPassword.message} @@ -369,13 +402,30 @@ export default function ProfilePage() { > {t('passwordForm.confirm')} - +
{passwordForm.formState.errors.confirmPassword.message} diff --git a/apps/frontend/app/[locale]/login/page.tsx b/apps/frontend/app/[locale]/login/page.tsx index 17e4fe3..106ed1c 100644 --- a/apps/frontend/app/[locale]/login/page.tsx +++ b/apps/frontend/app/[locale]/login/page.tsx @@ -5,6 +5,7 @@ import { Link } from '@/i18n/navigation'; import Image from 'next/image'; import { useSearchParams } from 'next/navigation'; import { useTranslations } from 'next-intl'; +import { Eye, EyeOff } from 'lucide-react'; import { useAuth } from '@/lib/context/auth-context'; function AnimatedCounter({ @@ -97,9 +98,11 @@ function LoginPageContent() { const tLogin = useTranslations('auth.login'); const tPanel = useTranslations('auth.sidePanel'); const tFooter = useTranslations('auth.footerLinks'); + const tCommon = useTranslations('common'); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [rememberMe, setRememberMe] = useState(false); + const [showPassword, setShowPassword] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const [fieldErrors, setFieldErrors] = useState({}); @@ -262,22 +265,33 @@ function LoginPageContent() { > {tLogin('passwordLabel')} - + + + setShowPassword(!showPassword)} + className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-500 hover:text-neutral-700" + tabIndex={-1} + aria-label={showPassword ? tCommon('hidePassword') : tCommon('showPassword')} + > + {showPassword ? : } + + {fieldErrors.password && ( (1); const [statsActive, setStatsActive] = useState(false); @@ -81,6 +83,8 @@ function RegisterPageContent() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [organizationName, setOrganizationName] = useState(''); const [organizationType, setOrganizationType] = useState('FREIGHT_FORWARDER'); @@ -513,17 +517,28 @@ function RegisterPageContent() { {t('passwordLabel')} - setPassword(e.target.value)} - className="input w-full" - placeholder={t('passwordPlaceholder')} - autoComplete="new-password" - disabled={isLoading} - /> + + setPassword(e.target.value)} + className="input w-full pr-12" + placeholder={t('passwordPlaceholder')} + autoComplete="new-password" + disabled={isLoading} + /> + setShowPassword(!showPassword)} + className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-500 hover:text-neutral-700" + tabIndex={-1} + aria-label={showPassword ? tCommon('hidePassword') : tCommon('showPassword')} + > + {showPassword ? : } + + {t('passwordHint')}
(1); const [statsActive, setStatsActive] = useState(false); @@ -81,6 +83,8 @@ function RegisterPageContent() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [organizationName, setOrganizationName] = useState(''); const [organizationType, setOrganizationType] = useState('FREIGHT_FORWARDER'); @@ -513,17 +517,28 @@ function RegisterPageContent() { {t('passwordLabel')} - setPassword(e.target.value)} - className="input w-full" - placeholder={t('passwordPlaceholder')} - autoComplete="new-password" - disabled={isLoading} - /> + + setPassword(e.target.value)} + className="input w-full pr-12" + placeholder={t('passwordPlaceholder')} + autoComplete="new-password" + disabled={isLoading} + /> + setShowPassword(!showPassword)} + className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-500 hover:text-neutral-700" + tabIndex={-1} + aria-label={showPassword ? tCommon('hidePassword') : tCommon('showPassword')} + > + {showPassword ? : } + + {t('passwordHint')}
{t('passwordHint')}