rage-framework-example/apps/browser/src/hooks/useLoginForm.ts
2025-03-28 17:03:23 +02:00

140 lines
5.1 KiB
TypeScript

import React, { useState, useCallback } from 'react'
import { loginSchema, LoginFormData, FieldErrors } from '../validation'
import { ZodError } from 'zod'
interface UseLoginFormProps {
onSubmitSuccess?: (data: LoginFormData) => void // Optional callback for successful submission
}
export function useLoginForm({ onSubmitSuccess }: UseLoginFormProps = {}) {
const [formData, setFormData] = useState<LoginFormData>({
identifier: '', // Can hold email or username
password: '',
saveLogin: false,
savePassword: false,
})
const [errors, setErrors] = useState<FieldErrors<LoginFormData>>({})
const [isLoading, setIsLoading] = useState(false)
const [submitError, setSubmitError] = useState<string | null>(null)
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = e.target
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value,
}))
// Clear error for the field being edited
if (errors[name as keyof LoginFormData]) {
setErrors(prev => ({ ...prev, [name]: undefined }))
}
// Clear general submission error
if (submitError) {
setSubmitError(null)
}
},
[errors, submitError],
)
const validateForm = useCallback(() => {
try {
loginSchema.parse(formData)
setErrors({})
return true
} catch (error) {
if (error instanceof ZodError) {
const fieldErrors: FieldErrors<LoginFormData> = {}
error.errors.forEach(err => {
if (err.path.length > 0) {
fieldErrors[err.path[0] as keyof LoginFormData] =
err.message
}
})
setErrors(fieldErrors)
} else {
console.error(
'An unexpected error occurred during validation:',
error,
)
setSubmitError('An unexpected validation error occurred.')
}
return false
}
}, [formData])
const handleSubmit = useCallback(
async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setSubmitError(null)
if (!validateForm()) {
return
}
setIsLoading(true)
console.log('Submitting login data:', formData)
// --- TODO: Replace with actual API call to your RageMP server ---
try {
// Example: Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
// Assuming API returns success:
console.log('Login successful!')
// Here you would typically receive a token or session info
// Handle 'saveLogin' and 'savePassword' (e.g., using localStorage/sessionStorage)
if (formData.saveLogin) {
localStorage.setItem('savedUsername', formData.identifier) // Example
} else {
localStorage.removeItem('savedUsername') // Example
}
// Password saving is generally discouraged for security reasons,
// but if required:
if (formData.savePassword) {
// Be VERY careful with storing passwords. Consider secure storage or tokens.
// localStorage.setItem('savedPassword', formData.password); // **Highly discouraged**
console.warn(
'Password saving enabled - ensure secure storage mechanism.',
)
}
setErrors({}) // Clear errors on success
if (onSubmitSuccess) {
onSubmitSuccess(formData) // Call success callback
}
// You might redirect the user or update application state here
// --- Mock Error Handling (remove in real implementation) ---
// if (formData.identifier === 'wrong') {
// throw new Error("Invalid credentials.");
// }
// --- End Mock Error Handling ---
} catch (apiError: unknown) {
console.error('Login API error:', apiError)
setSubmitError('Login failed. Please check your credentials.')
} finally {
setIsLoading(false)
}
// --- End API call section ---
},
[formData, validateForm, onSubmitSuccess],
)
// Effect to potentially load saved username on initial render (example)
// useEffect(() => {
// const savedUser = localStorage.getItem('savedUsername');
// if (savedUser) {
// setFormData(prev => ({ ...prev, identifier: savedUser, saveLogin: true }));
// }
// }, []);
return {
formData,
errors,
isLoading,
submitError,
handleChange,
handleSubmit,
}
}