← back to Sdcc Awards

cypressaward/frontend/app/browse-opportunities/page.tsx

381 lines

'use client'

import { useState, useEffect } from 'react'
import { Card } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Calendar, DollarSign, Building, Clock, TrendingUp, AlertCircle, Search } from 'lucide-react'
import { supabase } from '@/lib/supabase'

interface Opportunity {
  id: number
  case_name: string
  court: string
  settlement_amount: number
  estimated_cypres: number
  deadline: string
  status: 'Open' | 'Pending' | 'Closed'
  category: string
  description: string
  eligibility: string[]
  law_firm: string
}

export default function BrowseOpportunitiesPage() {
  const [opportunities, setOpportunities] = useState<Opportunity[]>([])
  const [filteredOpps, setFilteredOpps] = useState<Opportunity[]>([])
  const [loading, setLoading] = useState(true)
  const [searchTerm, setSearchTerm] = useState('')
  const [selectedCategory, setSelectedCategory] = useState('all')
  const [selectedStatus, setSelectedStatus] = useState('all')

  // Mock data for opportunities
  const mockOpportunities: Opportunity[] = [
    {
      id: 1,
      case_name: 'In re: Meta Pixel Privacy Litigation',
      court: 'N.D. Cal.',
      settlement_amount: 90000000,
      estimated_cypres: 15000000,
      deadline: '2024-12-31',
      status: 'Open',
      category: 'Privacy & Data Security',
      description: 'Settlement regarding unauthorized data collection through Meta Pixel tracking technology',
      eligibility: ['Privacy advocacy organizations', 'Consumer protection nonprofits', 'Digital rights groups'],
      law_firm: 'Lieff Cabraser Heimann & Bernstein'
    },
    {
      id: 2,
      case_name: 'Wells Fargo Overdraft Fee Settlement',
      court: 'S.D.N.Y.',
      settlement_amount: 75000000,
      estimated_cypres: 8000000,
      deadline: '2024-11-15',
      status: 'Open',
      category: 'Consumer Protection',
      description: 'Class action settlement for improper overdraft fee practices',
      eligibility: ['Financial literacy organizations', 'Consumer advocacy groups', 'Legal aid societies'],
      law_firm: 'Berger Montague'
    },
    {
      id: 3,
      case_name: 'Student Loan Servicing Practices',
      court: 'E.D. Pa.',
      settlement_amount: 55000000,
      estimated_cypres: 12000000,
      deadline: '2025-01-20',
      status: 'Open',
      category: 'Education & Student Rights',
      description: 'Settlement addressing deceptive student loan servicing practices',
      eligibility: ['Student advocacy organizations', 'Education nonprofits', 'Debt counseling services'],
      law_firm: 'Cohen Milstein'
    },
    {
      id: 4,
      case_name: 'Healthcare Data Breach Settlement',
      court: 'C.D. Cal.',
      settlement_amount: 120000000,
      estimated_cypres: 25000000,
      deadline: '2024-10-30',
      status: 'Pending',
      category: 'Healthcare',
      description: 'Major healthcare system data breach affecting millions of patients',
      eligibility: ['Healthcare advocacy groups', 'Patient rights organizations', 'Privacy nonprofits'],
      law_firm: 'Keller Rohrback'
    },
    {
      id: 5,
      case_name: 'Environmental Contamination Settlement',
      court: 'D. N.J.',
      settlement_amount: 200000000,
      estimated_cypres: 35000000,
      deadline: '2025-02-28',
      status: 'Open',
      category: 'Environmental Protection',
      description: 'Settlement for groundwater contamination affecting local communities',
      eligibility: ['Environmental nonprofits', 'Community health organizations', 'Clean water advocacy groups'],
      law_firm: 'Weitz & Luxenberg'
    },
    {
      id: 6,
      case_name: 'Workplace Discrimination Class Action',
      court: 'N.D. Ill.',
      settlement_amount: 45000000,
      estimated_cypres: 6000000,
      deadline: '2024-09-15',
      status: 'Closed',
      category: 'Workers Rights',
      description: 'Settlement addressing systemic workplace discrimination',
      eligibility: ['Workers rights organizations', 'Civil rights groups', 'Employment law clinics'],
      law_firm: 'Sanford Heisler Sharp'
    }
  ]

  useEffect(() => {
    // In production, fetch from Supabase
    setOpportunities(mockOpportunities)
    setFilteredOpps(mockOpportunities)
    setLoading(false)
  }, [])

  useEffect(() => {
    let filtered = opportunities

    // Search filter
    if (searchTerm) {
      filtered = filtered.filter(opp =>
        opp.case_name.toLowerCase().includes(searchTerm.toLowerCase()) ||
        opp.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
        opp.category.toLowerCase().includes(searchTerm.toLowerCase())
      )
    }

    // Category filter
    if (selectedCategory !== 'all') {
      filtered = filtered.filter(opp => opp.category === selectedCategory)
    }

    // Status filter
    if (selectedStatus !== 'all') {
      filtered = filtered.filter(opp => opp.status === selectedStatus)
    }

    setFilteredOpps(filtered)
  }, [opportunities, searchTerm, selectedCategory, selectedStatus])

  const categories = Array.from(new Set(opportunities.map(o => o.category)))
  
  const formatCurrency = (amount: number) => {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD',
      minimumFractionDigits: 0,
      maximumFractionDigits: 0,
    }).format(amount)
  }

  const getDaysRemaining = (deadline: string) => {
    const today = new Date()
    const deadlineDate = new Date(deadline)
    const diffTime = deadlineDate.getTime() - today.getTime()
    const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
    return diffDays
  }

  const getStatusColor = (status: string) => {
    switch(status) {
      case 'Open': return 'bg-green-100 text-green-800'
      case 'Pending': return 'bg-yellow-100 text-yellow-800'
      case 'Closed': return 'bg-gray-100 text-gray-800'
      default: return 'bg-gray-100 text-gray-800'
    }
  }

  return (
    <div className="min-h-screen bg-gray-50 py-12">
      <div className="max-w-7xl mx-auto px-4">
        <div className="text-center mb-8">
          <h1 className="text-4xl font-bold mb-4">Browse Cy Pres Opportunities</h1>
          <p className="text-xl text-gray-600">
            Active and upcoming cy pres distributions from class action settlements
          </p>
        </div>

        {/* Stats Cards */}
        <div className="grid md:grid-cols-4 gap-4 mb-8">
          <Card className="p-4">
            <div className="flex items-center justify-between">
              <div>
                <p className="text-sm text-gray-600">Active Opportunities</p>
                <p className="text-2xl font-bold">{opportunities.filter(o => o.status === 'Open').length}</p>
              </div>
              <TrendingUp className="h-8 w-8 text-green-500" />
            </div>
          </Card>
          <Card className="p-4">
            <div className="flex items-center justify-between">
              <div>
                <p className="text-sm text-gray-600">Total Available</p>
                <p className="text-2xl font-bold">
                  {formatCurrency(opportunities.reduce((sum, o) => sum + o.estimated_cypres, 0))}
                </p>
              </div>
              <DollarSign className="h-8 w-8 text-blue-500" />
            </div>
          </Card>
          <Card className="p-4">
            <div className="flex items-center justify-between">
              <div>
                <p className="text-sm text-gray-600">Closing Soon</p>
                <p className="text-2xl font-bold">
                  {opportunities.filter(o => getDaysRemaining(o.deadline) <= 30 && o.status === 'Open').length}
                </p>
              </div>
              <Clock className="h-8 w-8 text-orange-500" />
            </div>
          </Card>
          <Card className="p-4">
            <div className="flex items-center justify-between">
              <div>
                <p className="text-sm text-gray-600">Categories</p>
                <p className="text-2xl font-bold">{categories.length}</p>
              </div>
              <Building className="h-8 w-8 text-purple-500" />
            </div>
          </Card>
        </div>

        {/* Filters */}
        <Card className="p-6 mb-8">
          <div className="flex flex-col md:flex-row gap-4">
            <div className="flex-1">
              <div className="relative">
                <Search className="absolute left-3 top-3 h-4 w-4 text-gray-400" />
                <input
                  type="text"
                  placeholder="Search opportunities..."
                  className="w-full pl-10 pr-4 py-2 border rounded-md"
                  value={searchTerm}
                  onChange={(e) => setSearchTerm(e.target.value)}
                />
              </div>
            </div>
            <select
              className="px-4 py-2 border rounded-md"
              value={selectedCategory}
              onChange={(e) => setSelectedCategory(e.target.value)}
            >
              <option value="all">All Categories</option>
              {categories.map(cat => (
                <option key={cat} value={cat}>{cat}</option>
              ))}
            </select>
            <select
              className="px-4 py-2 border rounded-md"
              value={selectedStatus}
              onChange={(e) => setSelectedStatus(e.target.value)}
            >
              <option value="all">All Status</option>
              <option value="Open">Open</option>
              <option value="Pending">Pending</option>
              <option value="Closed">Closed</option>
            </select>
          </div>
        </Card>

        {/* Opportunities List */}
        <div className="space-y-6">
          {loading ? (
            <div className="text-center py-12">
              <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
            </div>
          ) : filteredOpps.length === 0 ? (
            <Card className="p-12 text-center">
              <AlertCircle className="h-12 w-12 text-gray-400 mx-auto mb-4" />
              <p className="text-gray-600">No opportunities match your search criteria</p>
            </Card>
          ) : (
            filteredOpps.map(opp => {
              const daysRemaining = getDaysRemaining(opp.deadline)
              return (
                <Card key={opp.id} className="p-6 hover:shadow-lg transition-shadow">
                  <div className="flex justify-between items-start mb-4">
                    <div className="flex-1">
                      <div className="flex items-center gap-3 mb-2">
                        <Badge className={getStatusColor(opp.status)}>
                          {opp.status}
                        </Badge>
                        <Badge variant="outline">{opp.category}</Badge>
                        {daysRemaining <= 30 && opp.status === 'Open' && (
                          <Badge className="bg-orange-100 text-orange-800">
                            <Clock className="h-3 w-3 mr-1" />
                            {daysRemaining} days left
                          </Badge>
                        )}
                      </div>
                      <h3 className="text-xl font-bold mb-2">{opp.case_name}</h3>
                      <p className="text-gray-600 mb-3">{opp.description}</p>
                      
                      <div className="grid md:grid-cols-3 gap-4 mb-4">
                        <div>
                          <p className="text-sm text-gray-500">Court</p>
                          <p className="font-medium">{opp.court}</p>
                        </div>
                        <div>
                          <p className="text-sm text-gray-500">Total Settlement</p>
                          <p className="font-medium">{formatCurrency(opp.settlement_amount)}</p>
                        </div>
                        <div>
                          <p className="text-sm text-gray-500">Est. Cy Pres Amount</p>
                          <p className="font-bold text-green-600">{formatCurrency(opp.estimated_cypres)}</p>
                        </div>
                      </div>

                      <div className="mb-4">
                        <p className="text-sm text-gray-500 mb-2">Eligible Organizations:</p>
                        <div className="flex flex-wrap gap-2">
                          {opp.eligibility.map((elig, idx) => (
                            <Badge key={idx} variant="secondary" className="text-xs">
                              {elig}
                            </Badge>
                          ))}
                        </div>
                      </div>

                      <div className="flex items-center justify-between">
                        <div className="text-sm text-gray-500">
                          Law Firm: <span className="font-medium text-gray-700">{opp.law_firm}</span>
                        </div>
                        <div className="flex gap-2">
                          <Button variant="outline" size="sm">
                            View Details
                          </Button>
                          {opp.status === 'Open' && (
                            <Button size="sm">
                              Apply Now
                            </Button>
                          )}
                        </div>
                      </div>
                    </div>
                  </div>

                  {opp.status === 'Open' && (
                    <div className="mt-4 pt-4 border-t">
                      <div className="flex items-center justify-between text-sm">
                        <span className="text-gray-600">
                          Application Deadline: <span className="font-medium">{new Date(opp.deadline).toLocaleDateString()}</span>
                        </span>
                        {daysRemaining > 0 && (
                          <span className={`font-medium ${daysRemaining <= 7 ? 'text-red-600' : 'text-gray-600'}`}>
                            {daysRemaining} days remaining
                          </span>
                        )}
                      </div>
                    </div>
                  )}
                </Card>
              )
            })
          )}
        </div>

        {/* Help Section */}
        <Card className="mt-12 p-8 bg-blue-50">
          <h2 className="text-2xl font-bold mb-4">Need Help Applying?</h2>
          <p className="text-gray-700 mb-4">
            Our team can guide you through the cy pres application process and help maximize your chances of receiving funding.
          </p>
          <div className="flex gap-4">
            <Button>
              Schedule Consultation
            </Button>
            <Button variant="outline">
              Download Application Guide
            </Button>
          </div>
        </Card>
      </div>
    </div>
  )
}