140 lines
4.3 KiB
TypeScript
140 lines
4.3 KiB
TypeScript
/**
|
|
* Reset Password Client Component
|
|
*
|
|
* Handles client-side reset password flow.
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
import { ResetPasswordViewData } from '@/lib/builders/view-data/types/ResetPasswordViewData';
|
|
import { ResetPasswordTemplate } from '@/templates/auth/ResetPasswordTemplate';
|
|
import { ResetPasswordMutation } from '@/lib/mutations/auth/ResetPasswordMutation';
|
|
import { ResetPasswordViewModelBuilder } from '@/lib/builders/view-models/ResetPasswordViewModelBuilder';
|
|
import { ResetPasswordViewModel } from '@/lib/view-models/auth/ResetPasswordViewModel';
|
|
import { routes } from '@/lib/routing/RouteConfig';
|
|
|
|
interface ResetPasswordClientProps {
|
|
viewData: ResetPasswordViewData;
|
|
}
|
|
|
|
export function ResetPasswordClient({ viewData }: ResetPasswordClientProps) {
|
|
const router = useRouter();
|
|
const searchParams = useSearchParams();
|
|
|
|
// Build ViewModel from ViewData
|
|
const [viewModel, setViewModel] = useState<ResetPasswordViewModel>(() =>
|
|
ResetPasswordViewModelBuilder.build(viewData)
|
|
);
|
|
|
|
const [formData, setFormData] = useState({
|
|
newPassword: '',
|
|
confirmPassword: ''
|
|
});
|
|
|
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
|
|
// Validate passwords match
|
|
if (formData.newPassword !== formData.confirmPassword) {
|
|
setViewModel(prev => prev.withMutationState(false, 'Passwords do not match'));
|
|
return;
|
|
}
|
|
|
|
// Update submitting state
|
|
setViewModel(prev => prev.withMutationState(true, null));
|
|
|
|
try {
|
|
const token = searchParams.get('token');
|
|
if (!token) {
|
|
setViewModel(prev => prev.withMutationState(false, 'Invalid reset link'));
|
|
return;
|
|
}
|
|
|
|
// Execute reset password mutation
|
|
const mutation = new ResetPasswordMutation();
|
|
const result = await mutation.execute({
|
|
token,
|
|
newPassword: formData.newPassword,
|
|
});
|
|
|
|
if (result.isErr()) {
|
|
const error = result.getError();
|
|
setViewModel(prev => prev.withMutationState(false, error));
|
|
return;
|
|
}
|
|
|
|
// Success
|
|
const data = result.unwrap();
|
|
setViewModel(prev => prev.withSuccess(data.message));
|
|
|
|
// Redirect to login after a delay
|
|
setTimeout(() => {
|
|
router.push(routes.auth.login);
|
|
}, 3000);
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Failed to reset password';
|
|
setViewModel(prev => prev.withMutationState(false, errorMessage));
|
|
}
|
|
};
|
|
|
|
// Toggle password visibility
|
|
const togglePassword = () => {
|
|
setViewModel(prev => prev.withUIState({
|
|
...prev.uiState,
|
|
showPassword: !prev.uiState.showPassword,
|
|
}));
|
|
};
|
|
|
|
const toggleConfirmPassword = () => {
|
|
setViewModel(prev => prev.withUIState({
|
|
...prev.uiState,
|
|
showConfirmPassword: !prev.uiState.showConfirmPassword,
|
|
}));
|
|
};
|
|
|
|
// Build viewData for template
|
|
const templateViewData: ResetPasswordViewData = {
|
|
...viewData,
|
|
showSuccess: viewModel.showSuccess,
|
|
successMessage: viewModel.successMessage || undefined,
|
|
formState: viewModel.formState,
|
|
isSubmitting: viewModel.isSubmitting,
|
|
submitError: viewModel.submitError,
|
|
};
|
|
|
|
return (
|
|
<ResetPasswordTemplate
|
|
// Spread the viewData properties
|
|
token={templateViewData.token}
|
|
returnTo={templateViewData.returnTo}
|
|
showSuccess={templateViewData.showSuccess}
|
|
successMessage={templateViewData.successMessage}
|
|
formState={templateViewData.formState}
|
|
isSubmitting={templateViewData.isSubmitting}
|
|
submitError={templateViewData.submitError}
|
|
// Add the additional props
|
|
formActions={{
|
|
setFormData,
|
|
handleSubmit,
|
|
setShowSuccess: (show) => {
|
|
if (!show) {
|
|
// Reset to initial state
|
|
setViewModel(() => ResetPasswordViewModelBuilder.build(viewData));
|
|
}
|
|
},
|
|
setShowPassword: togglePassword,
|
|
setShowConfirmPassword: toggleConfirmPassword,
|
|
}}
|
|
uiState={{
|
|
showPassword: viewModel.uiState.showPassword,
|
|
showConfirmPassword: viewModel.uiState.showConfirmPassword,
|
|
}}
|
|
mutationState={{
|
|
isPending: viewModel.mutationPending,
|
|
error: viewModel.mutationError,
|
|
}}
|
|
/>
|
|
);
|
|
} |