diff --git a/application/src/components/auth/ForgotPasswordDialog.tsx b/application/src/components/auth/ForgotPasswordDialog.tsx new file mode 100644 index 0000000..1afad40 --- /dev/null +++ b/application/src/components/auth/ForgotPasswordDialog.tsx @@ -0,0 +1,259 @@ +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { toast } from '@/components/ui/use-toast'; +import { Mail, ArrowLeft } from 'lucide-react'; +import { useLanguage } from '@/contexts/LanguageContext'; +import { getCurrentEndpoint } from '@/lib/pocketbase'; + +interface ForgotPasswordDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function ForgotPasswordDialog({ open, onOpenChange }: ForgotPasswordDialogProps) { + const [step, setStep] = useState<'request' | 'confirm'>('request'); + const [email, setEmail] = useState(''); + const [token, setToken] = useState(''); + const [password, setPassword] = useState(''); + const [passwordConfirm, setPasswordConfirm] = useState(''); + const [loading, setLoading] = useState(false); + const { t } = useLanguage(); + + const handleRequestReset = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + + try { + const apiUrl = getCurrentEndpoint(); + const response = await fetch(`${apiUrl}/api/collections/_superusers/request-password-reset`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ email }), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.message || 'Failed to send reset email'); + } + + toast({ + title: "Reset Email Sent", + description: "Please check your email for password reset instructions.", + }); + setStep('confirm'); + } catch (error) { + console.error('Password reset request error:', error); + toast({ + variant: "destructive", + title: "Reset Failed", + description: error instanceof Error ? error.message : "Failed to send reset email. Please try again.", + }); + } finally { + setLoading(false); + } + }; + + const handleConfirmReset = async (e: React.FormEvent) => { + e.preventDefault(); + + if (password !== passwordConfirm) { + toast({ + variant: "destructive", + title: "Password Mismatch", + description: "Passwords do not match. Please try again.", + }); + return; + } + + if (password.length < 6) { + toast({ + variant: "destructive", + title: "Password Too Short", + description: "Password must be at least 6 characters long.", + }); + return; + } + + setLoading(true); + + try { + const apiUrl = getCurrentEndpoint(); + const response = await fetch(`${apiUrl}/api/collections/_superusers/confirm-password-reset`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + token, + password, + passwordConfirm + }), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.message || 'Failed to reset password'); + } + + toast({ + title: "Password Reset Successful", + description: "Your password has been reset successfully. You can now log in with your new password.", + }); + onOpenChange(false); + // Reset form state + setStep('request'); + setEmail(''); + setToken(''); + setPassword(''); + setPasswordConfirm(''); + } catch (error) { + console.error('Password reset confirmation error:', error); + toast({ + variant: "destructive", + title: "Reset Failed", + description: error instanceof Error ? error.message : "Failed to reset password. Please try again.", + }); + } finally { + setLoading(false); + } + }; + + const handleClose = () => { + onOpenChange(false); + // Reset form state when closing + setStep('request'); + setEmail(''); + setToken(''); + setPassword(''); + setPasswordConfirm(''); + }; + + return ( + + + + + {step === 'request' ? 'Reset Password' : 'Confirm Password Reset'} + + + {step === 'request' + ? 'Enter your email address and we\'ll send you a reset link.' + : 'Enter the reset token from your email and your new password.' + } + + + + {step === 'request' ? ( +
+
+ +
+
+ +
+ setEmail(e.target.value)} + required + className="pl-10" + /> +
+
+ +
+ + +
+
+ ) : ( +
+
+ + setToken(e.target.value)} + required + /> +
+ +
+ + setPassword(e.target.value)} + required + minLength={6} + /> +
+ +
+ + setPasswordConfirm(e.target.value)} + required + minLength={6} + /> +
+ +
+ + +
+
+ )} +
+
+ ); +} \ No newline at end of file