import React from 'react'; import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Lock } from 'lucide-react'; import { useMutation } from '@tanstack/react-query'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { Button } from '@tamagui/button'; import { ADMIN_LOGIN_PATH } from '../constants'; import { MobileCard } from './components/Primitives'; import { MobileField, MobileInput } from './components/FormControls'; import { ADMIN_GRADIENTS, useAdminTheme } from './theme'; import { useDocumentTitle } from './hooks/useDocumentTitle'; type ResetResponse = { status: string; }; type FieldErrors = Record; async function resetPassword(payload: { token: string; email: string; password: string; password_confirmation: string; }): Promise { const response = await fetch('/api/v1/tenant-auth/reset-password', { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, body: JSON.stringify(payload), }); if (response.status === 422) { const data = await response.json(); const errors = (data.errors ?? {}) as FieldErrors; const flattened = Object.values(errors).flat(); const message = flattened.join(' ') || 'Validation failed'; const error = new Error(message) as Error & { errors?: FieldErrors }; error.errors = errors; throw error; } if (!response.ok) { throw new Error('Request failed.'); } return (await response.json()) as ResetResponse; } export default function ResetPasswordPage() { const { t } = useTranslation('auth'); const navigate = useNavigate(); const { token } = useParams<{ token: string }>(); const location = useLocation(); const { text, muted, border, surface, accentSoft, primary, dangerBg, dangerText } = useAdminTheme(); const searchParams = React.useMemo(() => new URLSearchParams(location.search), [location.search]); const presetEmail = searchParams.get('email') ?? ''; const [email, setEmail] = React.useState(presetEmail); const [password, setPassword] = React.useState(''); const [passwordConfirmation, setPasswordConfirmation] = React.useState(''); const [status, setStatus] = React.useState(null); const [error, setError] = React.useState(null); const [fieldErrors, setFieldErrors] = React.useState({}); const safeAreaStyle: React.CSSProperties = { paddingTop: 'calc(env(safe-area-inset-top, 0px) + 16px)', paddingBottom: 'calc(env(safe-area-inset-bottom, 0px) + 16px)', }; useDocumentTitle(t('login.reset.title', 'Reset password')); const mutation = useMutation({ mutationKey: ['tenantAdminResetPassword'], mutationFn: resetPassword, onSuccess: (data) => { setFieldErrors({}); setError(null); setStatus(data.status ?? t('login.reset.success', 'Password updated.')); }, onError: (err: unknown) => { const typed = err as Error & { errors?: FieldErrors }; setFieldErrors(typed.errors ?? {}); setError(typed.message); }, }); const handleSubmit = () => { if (!token) { setError(t('login.reset.missing_token', 'Reset token missing.')); return; } setStatus(null); setError(null); mutation.mutate({ token, email, password, password_confirmation: passwordConfirmation, }); }; return ( {t('login.reset.title', 'Reset password')} {t('login.reset.copy', 'Choose a new password for your event admin account.')} setEmail(event.target.value)} placeholder={t('login.email_placeholder', 'name@example.com')} autoComplete="email" required /> setPassword(event.target.value)} placeholder={t('login.reset.password_placeholder', '••••••••')} autoComplete="new-password" required /> setPasswordConfirmation(event.target.value)} placeholder={t('login.reset.password_confirm_placeholder', '••••••••')} autoComplete="new-password" required /> {status ? ( {status} ) : null} {error ? ( {error} ) : null} ); }