← back to Sdcc Awards
cypressaward/frontend/app/nonprofit/register/page.tsx
359 lines
'use client'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { useRouter } from 'next/navigation'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Checkbox } from '@/components/ui/checkbox'
import { toast } from '@/components/ui/use-toast'
const nonprofitSchema = z.object({
organizationName: z.string().min(2, 'Organization name is required'),
ein: z.string().regex(/^\d{2}-\d{7}$/, 'EIN must be in format XX-XXXXXXX'),
website: z.string().url('Please enter a valid URL'),
phone: z.string().min(10, 'Please enter a valid phone number'),
email: z.string().email('Please enter a valid email'),
mission: z.string().min(50, 'Mission statement must be at least 50 characters'),
sectors: z.array(z.string()).min(1, 'Select at least one sector'),
programs: z.string().min(30, 'Please describe your programs'),
geography: z.array(z.string()).min(1, 'Select at least one geographic area'),
annualBudget: z.string().optional(),
yearFounded: z.string().regex(/^\d{4}$/, 'Please enter a valid year'),
contactName: z.string().min(2, 'Contact name is required'),
contactTitle: z.string().min(2, 'Contact title is required'),
contactEmail: z.string().email('Please enter a valid email'),
contactPhone: z.string().min(10, 'Please enter a valid phone number'),
cyPresExperience: z.string().optional(),
acceptTerms: z.boolean().refine(val => val === true, 'You must accept the terms'),
})
type NonprofitFormData = z.infer<typeof nonprofitSchema>
const sectors = [
'Education',
'Healthcare',
'Environment',
'Civil Rights',
'Consumer Protection',
'Privacy',
'Labor Rights',
'Housing',
'Financial Justice',
'Technology',
'Arts & Culture',
'Youth Services',
]
const geographicAreas = [
'National',
'Northeast',
'Southeast',
'Midwest',
'Southwest',
'West Coast',
'International',
]
export default function NonprofitRegisterPage() {
const router = useRouter()
const [isSubmitting, setIsSubmitting] = useState(false)
const [selectedSectors, setSelectedSectors] = useState<string[]>([])
const [selectedGeography, setSelectedGeography] = useState<string[]>([])
const {
register,
handleSubmit,
formState: { errors },
setValue,
} = useForm<NonprofitFormData>({
resolver: zodResolver(nonprofitSchema),
})
const onSubmit = async (data: NonprofitFormData) => {
setIsSubmitting(true)
try {
const response = await fetch('/api/nonprofits/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (response.ok) {
toast({
title: 'Registration Successful',
description: 'Your nonprofit has been registered. Check your email for verification.',
})
router.push('/nonprofit/dashboard')
} else {
throw new Error('Registration failed')
}
} catch (error) {
toast({
title: 'Registration Failed',
description: 'Please try again later.',
variant: 'destructive',
})
} finally {
setIsSubmitting(false)
}
}
return (
<div className="container max-w-4xl mx-auto py-8">
<Card>
<CardHeader>
<CardTitle>Nonprofit Registration</CardTitle>
<CardDescription>
Register your nonprofit to receive notifications about cy pres opportunities
and connect with law firms seeking eligible organizations.
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<div className="grid md:grid-cols-2 gap-4">
<div>
<Label htmlFor="organizationName">Organization Name *</Label>
<Input
id="organizationName"
{...register('organizationName')}
placeholder="Your Nonprofit Name"
/>
{errors.organizationName && (
<p className="text-red-500 text-sm mt-1">{errors.organizationName.message}</p>
)}
</div>
<div>
<Label htmlFor="ein">EIN (Tax ID) *</Label>
<Input
id="ein"
{...register('ein')}
placeholder="XX-XXXXXXX"
/>
{errors.ein && (
<p className="text-red-500 text-sm mt-1">{errors.ein.message}</p>
)}
</div>
<div>
<Label htmlFor="website">Website *</Label>
<Input
id="website"
{...register('website')}
placeholder="https://www.yournonprofit.org"
/>
{errors.website && (
<p className="text-red-500 text-sm mt-1">{errors.website.message}</p>
)}
</div>
<div>
<Label htmlFor="phone">Phone *</Label>
<Input
id="phone"
{...register('phone')}
placeholder="(555) 123-4567"
/>
{errors.phone && (
<p className="text-red-500 text-sm mt-1">{errors.phone.message}</p>
)}
</div>
<div>
<Label htmlFor="email">Organization Email *</Label>
<Input
id="email"
type="email"
{...register('email')}
placeholder="info@yournonprofit.org"
/>
{errors.email && (
<p className="text-red-500 text-sm mt-1">{errors.email.message}</p>
)}
</div>
<div>
<Label htmlFor="yearFounded">Year Founded *</Label>
<Input
id="yearFounded"
{...register('yearFounded')}
placeholder="2000"
/>
{errors.yearFounded && (
<p className="text-red-500 text-sm mt-1">{errors.yearFounded.message}</p>
)}
</div>
</div>
<div>
<Label htmlFor="mission">Mission Statement *</Label>
<Textarea
id="mission"
{...register('mission')}
rows={4}
placeholder="Describe your organization's mission and goals..."
/>
{errors.mission && (
<p className="text-red-500 text-sm mt-1">{errors.mission.message}</p>
)}
</div>
<div>
<Label>Sectors (Select all that apply) *</Label>
<div className="grid grid-cols-3 gap-3 mt-2">
{sectors.map((sector) => (
<div key={sector} className="flex items-center space-x-2">
<Checkbox
id={sector}
onCheckedChange={(checked) => {
const updated = checked
? [...selectedSectors, sector]
: selectedSectors.filter(s => s !== sector)
setSelectedSectors(updated)
setValue('sectors', updated)
}}
/>
<Label htmlFor={sector} className="text-sm font-normal cursor-pointer">
{sector}
</Label>
</div>
))}
</div>
{errors.sectors && (
<p className="text-red-500 text-sm mt-1">{errors.sectors.message}</p>
)}
</div>
<div>
<Label htmlFor="programs">Programs & Services *</Label>
<Textarea
id="programs"
{...register('programs')}
rows={3}
placeholder="Describe your main programs and services..."
/>
{errors.programs && (
<p className="text-red-500 text-sm mt-1">{errors.programs.message}</p>
)}
</div>
<div>
<Label>Geographic Coverage *</Label>
<div className="grid grid-cols-3 gap-3 mt-2">
{geographicAreas.map((area) => (
<div key={area} className="flex items-center space-x-2">
<Checkbox
id={area}
onCheckedChange={(checked) => {
const updated = checked
? [...selectedGeography, area]
: selectedGeography.filter(g => g !== area)
setSelectedGeography(updated)
setValue('geography', updated)
}}
/>
<Label htmlFor={area} className="text-sm font-normal cursor-pointer">
{area}
</Label>
</div>
))}
</div>
{errors.geography && (
<p className="text-red-500 text-sm mt-1">{errors.geography.message}</p>
)}
</div>
<div className="border-t pt-6">
<h3 className="text-lg font-semibold mb-4">Primary Contact Information</h3>
<div className="grid md:grid-cols-2 gap-4">
<div>
<Label htmlFor="contactName">Contact Name *</Label>
<Input
id="contactName"
{...register('contactName')}
placeholder="John Doe"
/>
{errors.contactName && (
<p className="text-red-500 text-sm mt-1">{errors.contactName.message}</p>
)}
</div>
<div>
<Label htmlFor="contactTitle">Title *</Label>
<Input
id="contactTitle"
{...register('contactTitle')}
placeholder="Executive Director"
/>
{errors.contactTitle && (
<p className="text-red-500 text-sm mt-1">{errors.contactTitle.message}</p>
)}
</div>
<div>
<Label htmlFor="contactEmail">Contact Email *</Label>
<Input
id="contactEmail"
type="email"
{...register('contactEmail')}
placeholder="john@yournonprofit.org"
/>
{errors.contactEmail && (
<p className="text-red-500 text-sm mt-1">{errors.contactEmail.message}</p>
)}
</div>
<div>
<Label htmlFor="contactPhone">Contact Phone *</Label>
<Input
id="contactPhone"
{...register('contactPhone')}
placeholder="(555) 123-4567"
/>
{errors.contactPhone && (
<p className="text-red-500 text-sm mt-1">{errors.contactPhone.message}</p>
)}
</div>
</div>
</div>
<div>
<Label htmlFor="cyPresExperience">Previous Cy Pres Experience (Optional)</Label>
<Textarea
id="cyPresExperience"
{...register('cyPresExperience')}
rows={3}
placeholder="Describe any previous cy pres awards received..."
/>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="acceptTerms"
onCheckedChange={(checked) => setValue('acceptTerms', checked as boolean)}
/>
<Label htmlFor="acceptTerms" className="text-sm font-normal">
I accept the terms of service and privacy policy, and certify that all information
provided is accurate. I understand this is not legal advice.
</Label>
</div>
{errors.acceptTerms && (
<p className="text-red-500 text-sm">{errors.acceptTerms.message}</p>
)}
<Button type="submit" className="w-full" disabled={isSubmitting}>
{isSubmitting ? 'Registering...' : 'Register Organization'}
</Button>
</form>
</CardContent>
</Card>
</div>
)
}