← back to Sdcc Awards
cypressaward/frontend/app/awards/page.tsx
229 lines
'use client'
import { useState, useEffect } from 'react'
interface Award {
id: number
recipient_organization: string
amount: number
amount_text: string
case_name: string
court: string
year: number
date_awarded: string
purpose: string
law_firm: string
settlement_administrator: string
source_url: string
source_title: string
verified: boolean
notes?: string
created_at: string
}
export default function AwardsPage() {
const [awards, setAwards] = useState<Award[]>([])
const [filteredAwards, setFilteredAwards] = useState<Award[]>([])
const [loading, setLoading] = useState(true)
const [searchTerm, setSearchTerm] = useState('')
useEffect(() => {
async function fetchAwards() {
// Always use the comprehensive data file with all 17 verified awards
const { allCyPresAwards } = await import('@/lib/all-awards-data')
const fallbackAwards = allCyPresAwards.map((award, index) => ({
id: index + 1,
recipient_organization: award.recipient_organization,
amount: award.amount,
amount_text: award.amount_text,
case_name: award.case_name,
court: award.court,
year: award.case_year,
date_awarded: `${award.case_year}-01-01`,
purpose: award.category || '',
law_firm: '',
settlement_administrator: '',
source_url: award.source_url || '',
source_title: award.case_name,
verified: true,
created_at: new Date().toISOString()
}))
// Sort by year (newest first), then by amount (largest first)
const sortedAwards = fallbackAwards.sort((a, b) => {
if (b.year !== a.year) return b.year - a.year;
return b.amount - a.amount;
});
setAwards(sortedAwards)
setFilteredAwards(sortedAwards)
setLoading(false)
}
fetchAwards()
}, [])
// Filter awards based on search criteria
useEffect(() => {
let filtered = awards
// Text search across multiple fields
if (searchTerm) {
const search = searchTerm.toLowerCase()
filtered = filtered.filter(award =>
award.recipient_organization.toLowerCase().includes(search) ||
award.case_name.toLowerCase().includes(search) ||
award.court.toLowerCase().includes(search) ||
award.purpose.toLowerCase().includes(search) ||
award.law_firm.toLowerCase().includes(search) ||
award.settlement_administrator.toLowerCase().includes(search) ||
(award.notes && award.notes.toLowerCase().includes(search))
)
}
setFilteredAwards(filtered)
}, [awards, searchTerm])
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(amount)
}
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
}
const totalAwards = filteredAwards.reduce((sum, award) => sum + award.amount, 0)
const clearSearch = () => {
setSearchTerm('')
}
if (loading) {
return (
<div className="container mx-auto px-4 py-8">
<div className="text-center">Loading cy pres awards...</div>
</div>
)
}
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-4xl font-bold mb-4 text-gray-900">Cy Pres Awards Database</h1>
<p className="text-lg text-gray-600 mb-6">
Verified real cy pres awards from class action settlements. All data sourced from court documents and official records.
</p>
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-lg p-6 mb-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-center">
<div>
<div className="text-3xl font-bold text-blue-600">{filteredAwards.length}</div>
<div className="text-sm text-gray-600">Filtered Awards</div>
</div>
<div>
<div className="text-3xl font-bold text-green-600">{formatCurrency(totalAwards)}</div>
<div className="text-sm text-gray-600">Total Distributed</div>
</div>
<div>
<div className="text-3xl font-bold text-purple-600">{new Set(filteredAwards.map(a => a.case_name)).size}</div>
<div className="text-sm text-gray-600">Unique Cases</div>
</div>
<div>
<div className="text-3xl font-bold text-orange-600">{new Set(filteredAwards.map(a => a.recipient_organization)).size}</div>
<div className="text-sm text-gray-600">Organizations</div>
</div>
</div>
</div>
{/* Search Bar */}
<div className="mb-6">
<div className="flex items-center space-x-4">
<div className="flex-1">
<input
type="text"
placeholder="Search awards by organization, case name, court, or category..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
{searchTerm && (
<button
onClick={clearSearch}
className="px-4 py-2 bg-gray-100 text-gray-600 rounded-lg hover:bg-gray-200 transition-colors"
>
Clear
</button>
)}
</div>
</div>
</div>
<div className="space-y-6">
{filteredAwards.map((award) => (
<div key={award.id} className="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
<div className="flex justify-between items-start mb-4">
<div className="flex-1">
<h3 className="text-xl font-bold text-gray-900 mb-2">
{award.recipient_organization}
</h3>
<p className="text-lg text-green-600 font-semibold mb-2">
{formatCurrency(award.amount)}
</p>
<p className="text-gray-700 font-medium mb-1">
{award.case_name}
</p>
<div className="flex flex-wrap gap-4 text-sm text-gray-600">
<span><strong>Court:</strong> {award.court}</span>
<span><strong>Year:</strong> {award.year}</span>
<span><strong>Category:</strong> {award.purpose}</span>
</div>
{award.source_url && (
<div className="mt-2">
<a
href={award.source_url}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-blue-600 hover:text-blue-800 text-sm"
>
View Article/Source
<svg className="ml-1 w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</a>
</div>
)}
</div>
<div className="text-right">
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
Verified ✓
</span>
</div>
</div>
</div>
))}
</div>
{filteredAwards.length === 0 && (
<div className="text-center py-12">
<div className="text-gray-500 text-lg">No awards found matching your criteria.</div>
<button
onClick={clearSearch}
className="mt-4 text-blue-600 hover:underline"
>
Clear all filters
</button>
</div>
)}
</div>
)
}