Files
miracles_in_motion/src/hooks/useDonationImpact.ts
T
defiQUGandCursor 22a889fb91 Align site copy with foundation brief, restore Portals nav, and audit public routes.
Portals link in nav/footer/mobile; foundation-aligned request assistance, donate, legal, and SEO; expanded sitemap; families-focused impact labels; fix document.title hook placement.

Co-authored-by: Cursor <[email protected]>
2026-05-26 22:33:06 -07:00

108 lines
3.0 KiB
TypeScript

import { useState, useEffect } from 'react'
// Types
export interface ImpactCalculation {
students: number
families: number
backpacks: number
clothing: number
emergency: number
annual: {
students: number
families: number
totalImpact: string
}
}
// Custom hook for donation impact calculations
export const useDonationImpact = (amount: number): ImpactCalculation => {
const [impact, setImpact] = useState<ImpactCalculation>({
students: 0,
families: 0,
backpacks: 0,
clothing: 0,
emergency: 0,
annual: {
students: 0,
families: 0,
totalImpact: '0 families supported annually'
}
})
useEffect(() => {
const calculateImpact = (donationAmount: number): ImpactCalculation => {
const families = Math.floor(donationAmount / 50) // $50 per family for comprehensive support
const students = families // legacy field — displayed as families in UI
const backpacks = Math.floor(donationAmount / 30) // outreach kits
const clothing = Math.floor(donationAmount / 45) // clothing & essentials
const emergency = Math.floor(donationAmount / 75) // emergency assistance
return {
students,
families,
backpacks,
clothing,
emergency,
annual: {
students: Math.floor((donationAmount * 12) / 50),
families: Math.floor((donationAmount * 12) / 50),
totalImpact: `${Math.floor((donationAmount * 12) / 50)} families supported annually`
}
}
}
setImpact(calculateImpact(amount))
}, [amount])
return impact
}
// Hook for form validation
export const useFormValidation = <T extends Record<string, any>>(
initialValues: T,
validationRules: Record<keyof T, (value: any) => string | null>
) => {
const [values, setValues] = useState<T>(initialValues)
const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({})
const [touched, setTouched] = useState<Partial<Record<keyof T, boolean>>>({})
const validate = (fieldName?: keyof T): boolean => {
const fieldsToValidate = fieldName ? [fieldName] : Object.keys(validationRules) as (keyof T)[]
const newErrors: Partial<Record<keyof T, string>> = { ...errors }
let isValid = true
fieldsToValidate.forEach((field) => {
const error = validationRules[field](values[field])
if (error) {
newErrors[field] = error
isValid = false
} else {
delete newErrors[field]
}
})
setErrors(newErrors)
return isValid
}
const setValue = (fieldName: keyof T, value: any): void => {
setValues(prev => ({ ...prev, [fieldName]: value }))
setTouched(prev => ({ ...prev, [fieldName]: true }))
}
const resetForm = (): void => {
setValues(initialValues)
setErrors({})
setTouched({})
}
return {
values,
errors,
touched,
setValue,
validate,
resetForm,
isValid: Object.keys(errors).length === 0
}
}