Add rate limiting, security headers, fix CORS for production
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
import { Heart, Shield, Camera, TreePine } from 'lucide-react';
|
||||
import { api, Campaign } from '@/lib/api';
|
||||
import DonationCard from '../components/DonationCard';
|
||||
|
||||
const FALLBACK_CAMPAIGNS: Campaign[] = [
|
||||
{
|
||||
id: '1', title: 'Land Preservation Fund',
|
||||
description: 'Help CCFW acquire and protect critical burrowing owl habitat before developers can build on it.',
|
||||
goal: 50000, raised: 31250,
|
||||
},
|
||||
{
|
||||
id: '2', title: 'Volunteer Equipment',
|
||||
description: 'Fund field monitors, spotting scopes, GPS units, and protective gear for our volunteer teams.',
|
||||
goal: 15000, raised: 9800,
|
||||
},
|
||||
{
|
||||
id: '3', title: 'Camera Infrastructure',
|
||||
description: 'Expand our network of wildlife cameras. Each camera streams 24/7 and reaches thousands of viewers.',
|
||||
goal: 25000, raised: 14200,
|
||||
},
|
||||
];
|
||||
|
||||
const icons = [TreePine, Shield, Camera];
|
||||
|
||||
async function getCampaigns(): Promise<Campaign[]> {
|
||||
try { return await api.getCampaigns(); }
|
||||
catch { return FALLBACK_CAMPAIGNS; }
|
||||
}
|
||||
|
||||
export default async function DonatePage() {
|
||||
const campaigns = await getCampaigns();
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-6 py-16 space-y-16">
|
||||
<header className="max-w-3xl space-y-4">
|
||||
<div className="flex items-center gap-2 text-gold text-sm font-bold uppercase tracking-widest">
|
||||
<Heart size={16} /> Support Conservation
|
||||
</div>
|
||||
<h1 className="text-4xl font-black text-white">Help Protect Cape Coral Wildlife</h1>
|
||||
<p className="text-stone-400 text-lg leading-relaxed">
|
||||
Every dollar goes directly to protecting burrowing owls, gopher tortoises, and native Florida wildlife. CCFW is a 501(c)(3) nonprofit — your donation is tax deductible.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
{/* Impact banner */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-px bg-white/5 rounded-2xl overflow-hidden">
|
||||
{[
|
||||
{ icon: icons[0], title: 'Land Preserved', value: '150+ acres' },
|
||||
{ icon: icons[1], title: 'Burrows Monitored', value: '2,000+' },
|
||||
{ icon: icons[2], title: 'Live Cameras', value: '12 active' },
|
||||
].map(({ icon: Icon, title, value }) => (
|
||||
<div key={title} className="bg-surfaceGreen p-8 flex flex-col items-center text-center gap-3">
|
||||
<Icon size={28} className="text-teal" />
|
||||
<div className="text-2xl font-black text-gold">{value}</div>
|
||||
<div className="text-xs text-stone-400 font-semibold uppercase tracking-wider">{title}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Campaign cards */}
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-black text-white">Active Campaigns</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{campaigns.map((c) => <DonationCard key={c.id} campaign={c} />)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Trust signals */}
|
||||
<div className="bg-surfaceGreen border border-white/5 rounded-2xl p-8 flex flex-col md:flex-row items-center gap-8">
|
||||
<Shield size={48} className="text-teal shrink-0" />
|
||||
<div>
|
||||
<h3 className="font-bold text-white text-xl mb-2">Secure & Tax-Deductible</h3>
|
||||
<p className="text-stone-400 text-sm leading-relaxed max-w-2xl">
|
||||
Cape Coral Friends of Wildlife is a registered 501(c)(3) nonprofit organization. All donations are tax-deductible to the fullest extent permitted by law. We never sell your information. Payment processing powered by Stripe.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user