fix mdp see
This commit is contained in:
parent
c6eaaf354a
commit
5aed7d81ce
@ -4,17 +4,19 @@ import { useState } from 'react';
|
|||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { Link, useRouter } from '@/i18n/navigation';
|
import { Link, useRouter } from '@/i18n/navigation';
|
||||||
import Image from 'next/image';
|
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 { login as apiLogin, getCurrentUser, logout as apiLogout } from '@/lib/api/auth';
|
||||||
import { useAuth } from '@/lib/context/auth-context';
|
import { useAuth } from '@/lib/context/auth-context';
|
||||||
|
|
||||||
export default function AdminLoginPage() {
|
export default function AdminLoginPage() {
|
||||||
const t = useTranslations('admin.login');
|
const t = useTranslations('admin.login');
|
||||||
|
const tCommon = useTranslations('common');
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { refreshUser } = useAuth();
|
const { refreshUser } = useAuth();
|
||||||
|
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
@ -94,17 +96,28 @@ export default function AdminLoginPage() {
|
|||||||
<label htmlFor="password" className="label">
|
<label htmlFor="password" className="label">
|
||||||
{t('passwordLabel')}
|
{t('passwordLabel')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type={showPassword ? 'text' : 'password'}
|
||||||
value={password}
|
value={password}
|
||||||
onChange={e => setPassword(e.target.value)}
|
onChange={e => setPassword(e.target.value)}
|
||||||
className="input w-full"
|
className="input w-full pr-12"
|
||||||
placeholder={t('passwordPlaceholder')}
|
placeholder={t('passwordPlaceholder')}
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => 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 ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { Eye, EyeOff } from 'lucide-react';
|
||||||
import { getAllUsers, updateAdminUser, deleteAdminUser } from '@/lib/api/admin';
|
import { getAllUsers, updateAdminUser, deleteAdminUser } from '@/lib/api/admin';
|
||||||
import { createUser } from '@/lib/api/users';
|
import { createUser } from '@/lib/api/users';
|
||||||
import { getAllOrganizations } from '@/lib/api/admin';
|
import { getAllOrganizations } from '@/lib/api/admin';
|
||||||
@ -27,8 +28,10 @@ interface Organization {
|
|||||||
|
|
||||||
export default function AdminUsersPage() {
|
export default function AdminUsersPage() {
|
||||||
const t = useTranslations('dashboard.admin.users');
|
const t = useTranslations('dashboard.admin.users');
|
||||||
|
const tCommon = useTranslations('common');
|
||||||
|
|
||||||
const [users, setUsers] = useState<User[]>([]);
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@ -349,12 +352,23 @@ export default function AdminUsersPage() {
|
|||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">
|
||||||
{t('modal.password')}
|
{t('modal.password')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative mt-1">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type={showPassword ? 'text' : 'password'}
|
||||||
value={formData.password}
|
value={formData.password}
|
||||||
onChange={e => setFormData({ ...formData, password: e.target.value })}
|
onChange={e => setFormData({ ...formData, password: e.target.value })}
|
||||||
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring-blue-500 focus:outline-none"
|
className="block w-full px-3 py-2 pr-10 border border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring-blue-500 focus:outline-none"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-label={showPassword ? tCommon('hidePassword') : tCommon('showPassword')}
|
||||||
|
>
|
||||||
|
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end space-x-2 pt-4">
|
<div className="flex justify-end space-x-2 pt-4">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -7,15 +7,20 @@ import { useForm } from 'react-hook-form';
|
|||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { Eye, EyeOff } from 'lucide-react';
|
||||||
import { updateUser, changePassword } from '@/lib/api';
|
import { updateUser, changePassword } from '@/lib/api';
|
||||||
|
|
||||||
export default function ProfilePage() {
|
export default function ProfilePage() {
|
||||||
const t = useTranslations('dashboard.profile');
|
const t = useTranslations('dashboard.profile');
|
||||||
|
const tCommon = useTranslations('common');
|
||||||
const { user, refreshUser, loading } = useAuth();
|
const { user, refreshUser, loading } = useAuth();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [activeTab, setActiveTab] = useState<'profile' | 'password'>('profile');
|
const [activeTab, setActiveTab] = useState<'profile' | 'password'>('profile');
|
||||||
const [successMessage, setSuccessMessage] = useState('');
|
const [successMessage, setSuccessMessage] = useState('');
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
const [errorMessage, setErrorMessage] = useState('');
|
||||||
|
const [showCurrentPassword, setShowCurrentPassword] = useState(false);
|
||||||
|
const [showNewPassword, setShowNewPassword] = useState(false);
|
||||||
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
|
||||||
const passwordSchema = z
|
const passwordSchema = z
|
||||||
.object({
|
.object({
|
||||||
@ -326,13 +331,30 @@ export default function ProfilePage() {
|
|||||||
>
|
>
|
||||||
{t('passwordForm.current')}
|
{t('passwordForm.current')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
{...passwordForm.register('currentPassword')}
|
{...passwordForm.register('currentPassword')}
|
||||||
type="password"
|
type={showCurrentPassword ? 'text' : 'password'}
|
||||||
id="currentPassword"
|
id="currentPassword"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-4 py-2 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowCurrentPassword(!showCurrentPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-label={
|
||||||
|
showCurrentPassword ? tCommon('hidePassword') : tCommon('showPassword')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{showCurrentPassword ? (
|
||||||
|
<EyeOff className="w-5 h-5" />
|
||||||
|
) : (
|
||||||
|
<Eye className="w-5 h-5" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{passwordForm.formState.errors.currentPassword && (
|
{passwordForm.formState.errors.currentPassword && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">
|
||||||
{passwordForm.formState.errors.currentPassword.message}
|
{passwordForm.formState.errors.currentPassword.message}
|
||||||
@ -347,13 +369,24 @@ export default function ProfilePage() {
|
|||||||
>
|
>
|
||||||
{t('passwordForm.new')}
|
{t('passwordForm.new')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
{...passwordForm.register('newPassword')}
|
{...passwordForm.register('newPassword')}
|
||||||
type="password"
|
type={showNewPassword ? 'text' : 'password'}
|
||||||
id="newPassword"
|
id="newPassword"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-4 py-2 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowNewPassword(!showNewPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-label={showNewPassword ? tCommon('hidePassword') : tCommon('showPassword')}
|
||||||
|
>
|
||||||
|
{showNewPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{passwordForm.formState.errors.newPassword && (
|
{passwordForm.formState.errors.newPassword && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">
|
||||||
{passwordForm.formState.errors.newPassword.message}
|
{passwordForm.formState.errors.newPassword.message}
|
||||||
@ -369,13 +402,30 @@ export default function ProfilePage() {
|
|||||||
>
|
>
|
||||||
{t('passwordForm.confirm')}
|
{t('passwordForm.confirm')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
{...passwordForm.register('confirmPassword')}
|
{...passwordForm.register('confirmPassword')}
|
||||||
type="password"
|
type={showConfirmPassword ? 'text' : 'password'}
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-4 py-2 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-label={
|
||||||
|
showConfirmPassword ? tCommon('hidePassword') : tCommon('showPassword')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{showConfirmPassword ? (
|
||||||
|
<EyeOff className="w-5 h-5" />
|
||||||
|
) : (
|
||||||
|
<Eye className="w-5 h-5" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{passwordForm.formState.errors.confirmPassword && (
|
{passwordForm.formState.errors.confirmPassword && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">
|
||||||
{passwordForm.formState.errors.confirmPassword.message}
|
{passwordForm.formState.errors.confirmPassword.message}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { Link } from '@/i18n/navigation';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { Eye, EyeOff } from 'lucide-react';
|
||||||
import { useAuth } from '@/lib/context/auth-context';
|
import { useAuth } from '@/lib/context/auth-context';
|
||||||
|
|
||||||
function AnimatedCounter({
|
function AnimatedCounter({
|
||||||
@ -97,9 +98,11 @@ function LoginPageContent() {
|
|||||||
const tLogin = useTranslations('auth.login');
|
const tLogin = useTranslations('auth.login');
|
||||||
const tPanel = useTranslations('auth.sidePanel');
|
const tPanel = useTranslations('auth.sidePanel');
|
||||||
const tFooter = useTranslations('auth.footerLinks');
|
const tFooter = useTranslations('auth.footerLinks');
|
||||||
|
const tCommon = useTranslations('common');
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [rememberMe, setRememberMe] = useState(false);
|
const [rememberMe, setRememberMe] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [fieldErrors, setFieldErrors] = useState<FieldErrors>({});
|
const [fieldErrors, setFieldErrors] = useState<FieldErrors>({});
|
||||||
@ -262,12 +265,13 @@ function LoginPageContent() {
|
|||||||
>
|
>
|
||||||
{tLogin('passwordLabel')}
|
{tLogin('passwordLabel')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type={showPassword ? 'text' : 'password'}
|
||||||
value={password}
|
value={password}
|
||||||
onChange={handlePasswordChange}
|
onChange={handlePasswordChange}
|
||||||
className={`input w-full ${
|
className={`input w-full pr-12 ${
|
||||||
fieldErrors.password
|
fieldErrors.password
|
||||||
? 'border-red-500 focus:border-red-500 focus:ring-red-500 bg-red-50'
|
? 'border-red-500 focus:border-red-500 focus:ring-red-500 bg-red-50'
|
||||||
: ''
|
: ''
|
||||||
@ -278,6 +282,16 @@ function LoginPageContent() {
|
|||||||
aria-invalid={!!fieldErrors.password}
|
aria-invalid={!!fieldErrors.password}
|
||||||
aria-describedby={fieldErrors.password ? 'password-error' : undefined}
|
aria-describedby={fieldErrors.password ? 'password-error' : undefined}
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => 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 ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{fieldErrors.password && (
|
{fieldErrors.password && (
|
||||||
<p
|
<p
|
||||||
id="password-error"
|
id="password-error"
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { useSearchParams } from 'next/navigation';
|
|||||||
import { Link, useRouter } from '@/i18n/navigation';
|
import { Link, useRouter } from '@/i18n/navigation';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { Eye, EyeOff } from 'lucide-react';
|
||||||
import { register } from '@/lib/api';
|
import { register } from '@/lib/api';
|
||||||
import { verifyInvitation, type InvitationResponse } from '@/lib/api/invitations';
|
import { verifyInvitation, type InvitationResponse } from '@/lib/api/invitations';
|
||||||
import type { OrganizationType } from '@/types/api';
|
import type { OrganizationType } from '@/types/api';
|
||||||
@ -57,6 +58,7 @@ function RegisterPageContent() {
|
|||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const t = useTranslations('auth.register');
|
const t = useTranslations('auth.register');
|
||||||
const tFooter = useTranslations('auth.footerLinks');
|
const tFooter = useTranslations('auth.footerLinks');
|
||||||
|
const tCommon = useTranslations('common');
|
||||||
|
|
||||||
const [step, setStep] = useState<1 | 2>(1);
|
const [step, setStep] = useState<1 | 2>(1);
|
||||||
const [statsActive, setStatsActive] = useState(false);
|
const [statsActive, setStatsActive] = useState(false);
|
||||||
@ -81,6 +83,8 @@ function RegisterPageContent() {
|
|||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [confirmPassword, setConfirmPassword] = useState('');
|
const [confirmPassword, setConfirmPassword] = useState('');
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
|
||||||
const [organizationName, setOrganizationName] = useState('');
|
const [organizationName, setOrganizationName] = useState('');
|
||||||
const [organizationType, setOrganizationType] = useState<OrganizationType>('FREIGHT_FORWARDER');
|
const [organizationType, setOrganizationType] = useState<OrganizationType>('FREIGHT_FORWARDER');
|
||||||
@ -513,17 +517,28 @@ function RegisterPageContent() {
|
|||||||
<label htmlFor="password" className="label">
|
<label htmlFor="password" className="label">
|
||||||
{t('passwordLabel')}
|
{t('passwordLabel')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type={showPassword ? 'text' : 'password'}
|
||||||
required
|
required
|
||||||
value={password}
|
value={password}
|
||||||
onChange={e => setPassword(e.target.value)}
|
onChange={e => setPassword(e.target.value)}
|
||||||
className="input w-full"
|
className="input w-full pr-12"
|
||||||
placeholder={t('passwordPlaceholder')}
|
placeholder={t('passwordPlaceholder')}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => 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 ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<p className="mt-1.5 text-body-xs text-neutral-500">{t('passwordHint')}</p>
|
<p className="mt-1.5 text-body-xs text-neutral-500">{t('passwordHint')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -531,17 +546,34 @@ function RegisterPageContent() {
|
|||||||
<label htmlFor="confirmPassword" className="label">
|
<label htmlFor="confirmPassword" className="label">
|
||||||
{t('confirmPasswordLabel')}
|
{t('confirmPasswordLabel')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
type="password"
|
type={showConfirmPassword ? 'text' : 'password'}
|
||||||
required
|
required
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onChange={e => setConfirmPassword(e.target.value)}
|
onChange={e => setConfirmPassword(e.target.value)}
|
||||||
className="input w-full"
|
className="input w-full pr-12"
|
||||||
placeholder={t('passwordPlaceholder')}
|
placeholder={t('passwordPlaceholder')}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-500 hover:text-neutral-700"
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-label={
|
||||||
|
showConfirmPassword ? tCommon('hidePassword') : tCommon('showPassword')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{showConfirmPassword ? (
|
||||||
|
<EyeOff className="w-5 h-5" />
|
||||||
|
) : (
|
||||||
|
<Eye className="w-5 h-5" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -5,16 +5,20 @@ import { useSearchParams } from 'next/navigation';
|
|||||||
import { Link, useRouter } from '@/i18n/navigation';
|
import { Link, useRouter } from '@/i18n/navigation';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { Eye, EyeOff } from 'lucide-react';
|
||||||
import { resetPassword } from '@/lib/api/auth';
|
import { resetPassword } from '@/lib/api/auth';
|
||||||
|
|
||||||
function ResetPasswordContent() {
|
function ResetPasswordContent() {
|
||||||
const t = useTranslations('auth.resetPassword');
|
const t = useTranslations('auth.resetPassword');
|
||||||
const tFooter = useTranslations('auth.footerLinks');
|
const tFooter = useTranslations('auth.footerLinks');
|
||||||
|
const tCommon = useTranslations('common');
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [token, setToken] = useState('');
|
const [token, setToken] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [confirmPassword, setConfirmPassword] = useState('');
|
const [confirmPassword, setConfirmPassword] = useState('');
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
const [success, setSuccess] = useState(false);
|
const [success, setSuccess] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -159,17 +163,28 @@ function ResetPasswordContent() {
|
|||||||
<label htmlFor="password" className="label">
|
<label htmlFor="password" className="label">
|
||||||
{t('passwordLabel')}
|
{t('passwordLabel')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type={showPassword ? 'text' : 'password'}
|
||||||
required
|
required
|
||||||
value={password}
|
value={password}
|
||||||
onChange={e => setPassword(e.target.value)}
|
onChange={e => setPassword(e.target.value)}
|
||||||
className="input w-full"
|
className="input w-full pr-12"
|
||||||
placeholder="••••••••••••"
|
placeholder="••••••••••••"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => 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 ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<p className="mt-1.5 text-body-xs text-neutral-500">{t('passwordHint')}</p>
|
<p className="mt-1.5 text-body-xs text-neutral-500">{t('passwordHint')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -177,17 +192,34 @@ function ResetPasswordContent() {
|
|||||||
<label htmlFor="confirmPassword" className="label">
|
<label htmlFor="confirmPassword" className="label">
|
||||||
{t('confirmPasswordLabel')}
|
{t('confirmPasswordLabel')}
|
||||||
</label>
|
</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
type="password"
|
type={showConfirmPassword ? 'text' : 'password'}
|
||||||
required
|
required
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onChange={e => setConfirmPassword(e.target.value)}
|
onChange={e => setConfirmPassword(e.target.value)}
|
||||||
className="input w-full"
|
className="input w-full pr-12"
|
||||||
placeholder="••••••••••••"
|
placeholder="••••••••••••"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-500 hover:text-neutral-700"
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-label={
|
||||||
|
showConfirmPassword ? tCommon('hidePassword') : tCommon('showPassword')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{showConfirmPassword ? (
|
||||||
|
<EyeOff className="w-5 h-5" />
|
||||||
|
) : (
|
||||||
|
<Eye className="w-5 h-5" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -25,7 +25,9 @@
|
|||||||
"discover": "Discover",
|
"discover": "Discover",
|
||||||
"tryNow": "Try now",
|
"tryNow": "Try now",
|
||||||
"continue": "Continue",
|
"continue": "Continue",
|
||||||
"submit": "Submit"
|
"submit": "Submit",
|
||||||
|
"showPassword": "Show password",
|
||||||
|
"hidePassword": "Hide password"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"title": "Administration",
|
"title": "Administration",
|
||||||
|
|||||||
@ -25,7 +25,9 @@
|
|||||||
"discover": "Découvrir",
|
"discover": "Découvrir",
|
||||||
"tryNow": "Essayer maintenant",
|
"tryNow": "Essayer maintenant",
|
||||||
"continue": "Continuer",
|
"continue": "Continuer",
|
||||||
"submit": "Envoyer"
|
"submit": "Envoyer",
|
||||||
|
"showPassword": "Afficher le mot de passe",
|
||||||
|
"hidePassword": "Masquer le mot de passe"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"title": "Espace administration",
|
"title": "Espace administration",
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { Eye, EyeOff } from 'lucide-react';
|
||||||
import { Carrier, CarrierStatus, CreateCarrierInput, UpdateCarrierInput } from '@/types/carrier';
|
import { Carrier, CarrierStatus, CreateCarrierInput, UpdateCarrierInput } from '@/types/carrier';
|
||||||
|
|
||||||
interface CarrierFormProps {
|
interface CarrierFormProps {
|
||||||
@ -25,6 +26,7 @@ export const CarrierForm: React.FC<CarrierFormProps> = ({ carrier, onSubmit, onC
|
|||||||
|
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [showApiKey, setShowApiKey] = useState(false);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -129,13 +131,24 @@ export const CarrierForm: React.FC<CarrierFormProps> = ({ carrier, onSubmit, onC
|
|||||||
{/* API Key */}
|
{/* API Key */}
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">API Key</label>
|
<label className="block text-sm font-medium text-gray-700 mb-1">API Key</label>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type={showApiKey ? 'text' : 'password'}
|
||||||
value={formData.apiKey}
|
value={formData.apiKey}
|
||||||
onChange={e => setFormData({ ...formData, apiKey: e.target.value })}
|
onChange={e => setFormData({ ...formData, apiKey: e.target.value })}
|
||||||
placeholder="Enter API key"
|
placeholder="Enter API key"
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowApiKey(!showApiKey)}
|
||||||
|
className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-label={showApiKey ? 'Hide API key' : 'Show API key'}
|
||||||
|
>
|
||||||
|
{showApiKey ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Rate Limit */}
|
{/* Rate Limit */}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user