← back to Sdcc Awards
cypressaward/frontend/app/nonprofits/page.tsx
323 lines
'use client'
import { useEffect, useState } from 'react'
import { supabase } from '@/lib/supabase'
interface NonProfit {
id: number
name: string
category: string
location: string
mission: string
rating: string
website: string
source: string
}
export default function NonProfitsPage() {
const [nonprofits, setNonprofits] = useState<NonProfit[]>([])
const [loading, setLoading] = useState(true)
const [selectedOrg, setSelectedOrg] = useState<NonProfit | null>(null)
const [showModal, setShowModal] = useState(false)
const [showRegisterForm, setShowRegisterForm] = useState(false)
useEffect(() => {
fetchNonprofits()
}, [])
async function fetchNonprofits() {
try {
const { data, error } = await supabase
.from('scraped_nonprofits')
.select('*')
.order('name', { ascending: true })
.limit(100)
if (error) throw error
setNonprofits(data || [])
} catch (error) {
console.error('Error fetching nonprofits:', error)
} finally {
setLoading(false)
}
}
function openModal(org: NonProfit) {
setSelectedOrg(org)
setShowModal(true)
}
function closeModal() {
setShowModal(false)
setSelectedOrg(null)
}
async function handleRegisterSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
const formData = new FormData(e.currentTarget)
const newOrg = {
name: formData.get('name') as string,
category: formData.get('category') as string,
location: formData.get('location') as string,
mission: formData.get('mission') as string,
rating: 'Pending Review',
website: formData.get('website') as string,
source: 'User Submission',
data_type: 'nonprofit',
hash: Math.random().toString(36).substring(7)
}
try {
const { error } = await supabase
.from('scraped_nonprofits')
.insert([newOrg])
if (!error) {
alert('Organization registered successfully!')
setShowRegisterForm(false)
fetchNonprofits()
}
} catch (error) {
console.error('Error registering nonprofit:', error)
alert('Error registering organization')
}
}
return (
<div className="min-h-screen bg-gray-50 py-8">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="bg-white shadow-xl rounded-lg">
<div className="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold text-gray-900">Non-Profit Organizations</h1>
<p className="text-gray-600 mt-2">Legal aid and advocacy organizations</p>
</div>
<button
onClick={() => setShowRegisterForm(true)}
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition font-medium"
>
Register Nonprofit
</button>
</div>
{loading ? (
<div className="flex justify-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 p-6">
{nonprofits.map((org) => (
<div key={org.id} className="bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-lg transition">
<div className="p-6">
<div className="mb-4">
<span className="inline-block px-3 py-1 text-xs font-semibold text-green-800 bg-green-100 rounded-full">
{org.category}
</span>
{org.rating && (
<span className="ml-2 inline-block px-3 py-1 text-xs font-semibold text-blue-800 bg-blue-100 rounded-full">
{org.rating}
</span>
)}
</div>
<h3 className="text-lg font-bold text-gray-900 mb-2">
{org.name}
</h3>
<p className="text-sm text-gray-600 mb-2">
<span className="font-semibold">Location:</span> {org.location}
</p>
<p className="text-gray-700 text-sm line-clamp-3 mb-4">
{org.mission}
</p>
<div className="flex gap-2">
<button
onClick={() => openModal(org)}
className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition text-sm font-medium"
>
View Details
</button>
{org.website && (
<a
href={org.website}
target="_blank"
rel="noopener noreferrer"
className="flex-1 px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition text-sm font-medium text-center"
>
Visit Site
</a>
)}
</div>
</div>
</div>
))}
</div>
)}
</div>
</div>
{/* Details Modal */}
{showModal && selectedOrg && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className="bg-white rounded-lg max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div className="p-6">
<div className="flex justify-between items-start mb-4">
<h2 className="text-2xl font-bold text-gray-900">{selectedOrg.name}</h2>
<button
onClick={closeModal}
className="text-gray-400 hover:text-gray-600"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div className="space-y-4">
<div>
<h3 className="font-semibold text-gray-700">Category</h3>
<p className="text-gray-900">{selectedOrg.category}</p>
</div>
<div>
<h3 className="font-semibold text-gray-700">Location</h3>
<p className="text-gray-900">{selectedOrg.location}</p>
</div>
<div>
<h3 className="font-semibold text-gray-700">Mission</h3>
<p className="text-gray-900">{selectedOrg.mission}</p>
</div>
<div>
<h3 className="font-semibold text-gray-700">Rating</h3>
<p className="text-gray-900">{selectedOrg.rating}</p>
</div>
<div>
<h3 className="font-semibold text-gray-700">Source</h3>
<p className="text-gray-900">{selectedOrg.source}</p>
</div>
</div>
<div className="mt-6 flex gap-3">
{selectedOrg.website && (
<a
href={selectedOrg.website}
target="_blank"
rel="noopener noreferrer"
className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition text-center font-medium"
>
Visit Website
</a>
)}
<button
onClick={closeModal}
className="flex-1 px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 transition font-medium"
>
Close
</button>
</div>
</div>
</div>
</div>
)}
{/* Register Form Modal */}
{showRegisterForm && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className="bg-white rounded-lg max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div className="p-6">
<div className="flex justify-between items-start mb-4">
<h2 className="text-2xl font-bold text-gray-900">Register Nonprofit Organization</h2>
<button
onClick={() => setShowRegisterForm(false)}
className="text-gray-400 hover:text-gray-600"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<form onSubmit={handleRegisterSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Organization Name *
</label>
<input
type="text"
name="name"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Category *
</label>
<select
name="category"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">Select a category</option>
<option value="Legal Aid">Legal Aid</option>
<option value="Civil Rights">Civil Rights</option>
<option value="Criminal Justice Reform">Criminal Justice Reform</option>
<option value="Immigration Law">Immigration Law</option>
<option value="Student Advocacy">Student Advocacy</option>
<option value="Environmental Law">Environmental Law</option>
<option value="Consumer Protection">Consumer Protection</option>
<option value="Other">Other</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Location *
</label>
<input
type="text"
name="location"
required
placeholder="City, State"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Mission Statement *
</label>
<textarea
name="mission"
required
rows={4}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Website
</label>
<input
type="url"
name="website"
placeholder="https://example.org"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="flex gap-3 pt-4">
<button
type="submit"
className="flex-1 px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition font-medium"
>
Register Organization
</button>
<button
type="button"
onClick={() => setShowRegisterForm(false)}
className="flex-1 px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 transition font-medium"
>
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
)}
</div>
)
}