BioNexus / client /src /pages /DirectoryPage.jsx
gaialive's picture
Upload 28 files
ad08f08 verified
import React, { useState, useEffect } from 'react';
const DirectoryPage = () => {
const [organizations, setOrganizations] = useState([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const [sectorFilter, setSectorFilter] = useState('All');
useEffect(() => {
// In a real app, this would fetch from an API
// For demo purposes, we'll use mock data
const mockOrganizations = [
{
id: 1,
name: 'Global Food Rescue Initiative',
sector: 'NGO',
country: 'International',
description: 'Non-profit organization focused on reducing food waste through redistribution networks.',
expertise: ['Food Recovery', 'Logistics', 'Volunteer Management'],
projects: 42
},
{
id: 2,
name: 'AgriTech Solutions Ltd',
sector: 'Private',
country: 'Netherlands',
description: 'Technology company developing smart cold chain solutions for perishable goods.',
expertise: ['Cold Chain Technology', 'IoT Sensors', 'Data Analytics'],
projects: 18
},
{
id: 3,
name: 'Sustainable Agriculture Research Institute',
sector: 'Academic',
country: 'United States',
description: 'Research institution studying post-harvest loss reduction techniques.',
expertise: ['Research', 'Post-harvest Technology', 'Training'],
projects: 27
},
{
id: 4,
name: 'Ministry of Agriculture and Food Security',
sector: 'Government',
country: 'Kenya',
description: 'Government agency implementing national food loss reduction programs.',
expertise: ['Policy Development', 'Program Implementation', 'Farmer Support'],
projects: 15
},
{
id: 5,
name: 'Circular Economy Food Partners',
sector: 'NGO',
country: 'Germany',
description: 'Organization promoting circular economy approaches to food systems.',
expertise: ['Circular Economy', 'Composting', 'Waste Valorization'],
projects: 33
},
{
id: 6,
name: 'Farm to Market Logistics Co.',
sector: 'Private',
country: 'Brazil',
description: 'Logistics company specializing in transportation of perishable agricultural products.',
expertise: ['Transportation', 'Cold Chain Logistics', 'Supply Chain'],
projects: 29
}
];
setTimeout(() => {
setOrganizations(mockOrganizations);
setLoading(false);
}, 500);
}, []);
const sectors = ['All', ...new Set(organizations.map(org => org.sector))];
const filteredOrganizations = organizations.filter(org => {
const matchesSearch = org.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
org.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
org.expertise.some(exp => exp.toLowerCase().includes(searchTerm.toLowerCase()));
const matchesSector = sectorFilter === 'All' || org.sector === sectorFilter;
return matchesSearch && matchesSector;
});
if (loading) {
return (
<div className="py-16 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 transition-colors duration-300 min-h-screen">
<div className="container mx-auto px-4">
<h2 className="text-3xl font-bold mb-8 text-gray-800 dark:text-white drop-shadow bg-gradient-to-r from-bio-green to-bio-blue bg-clip-text text-transparent">Stakeholder Directory</h2>
<div className="flex justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-bio-green"></div>
</div>
</div>
</div>
);
}
return (
<div className="py-16 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 transition-colors duration-300 min-h-screen">
<div className="container mx-auto px-4">
<h2 className="text-3xl font-bold mb-8 text-gray-800 dark:text-white drop-shadow bg-gradient-to-r from-bio-green to-bio-blue bg-clip-text text-transparent">Stakeholder Directory</h2>
<div className="mb-8 bg-white dark:bg-gray-700 rounded-2xl shadow-lg p-6 border border-gray-100 dark:border-gray-600 bg-gradient-to-br from-white to-gray-100 dark:from-gray-700 dark:to-gray-800">
<div className="flex flex-col md:flex-row gap-4">
<div className="flex-grow">
<input
type="text"
placeholder="Search organizations..."
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-bio-green focus:border-transparent dark:bg-gray-600 dark:text-white drop-shadow bg-gradient-to-r from-white to-gray-50 dark:from-gray-600 dark:to-gray-700"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div className="w-full md:w-48">
<select
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-bio-green focus:border-transparent dark:bg-gray-600 dark:text-white drop-shadow bg-gradient-to-r from-white to-gray-50 dark:from-gray-600 dark:to-gray-700"
value={sectorFilter}
onChange={(e) => setSectorFilter(e.target.value)}
>
{sectors.map(sector => (
<option key={sector} value={sector}>{sector}</option>
))}
</select>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredOrganizations.map((org) => (
<div key={org.id} className="bg-white dark:bg-gray-700 rounded-2xl shadow-lg overflow-hidden hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 border border-gray-100 dark:border-gray-600 bg-gradient-to-br from-white to-gray-100 dark:from-gray-700 dark:to-gray-800">
<div className="p-6">
<div className="flex justify-between items-start mb-4">
<h3 className="text-xl font-bold text-gray-800 dark:text-white drop-shadow bg-gradient-to-r from-gray-800 to-gray-600 bg-clip-text text-transparent dark:from-white dark:to-gray-300">{org.name}</h3>
<span className="inline-block px-3 py-1 text-xs font-semibold text-bio-purple bg-purple-100 dark:bg-purple-900/30 dark:text-purple-300 rounded-full drop-shadow bg-gradient-to-r from-purple-100 to-purple-200 dark:from-purple-900/30 dark:to-purple-800/30">
{org.sector}
</span>
</div>
<div className="mb-4">
<span className="text-sm text-gray-500 dark:text-gray-400 drop-shadow">{org.country}</span>
</div>
<p className="text-gray-600 dark:text-gray-300 mb-4 drop-shadow">{org.description}</p>
<div className="mb-4">
<h4 className="font-semibold text-gray-800 dark:text-white mb-2 drop-shadow bg-gradient-to-r from-gray-800 to-gray-600 bg-clip-text text-transparent dark:from-white dark:to-gray-300">Expertise:</h4>
<div className="flex flex-wrap gap-2">
{org.expertise.map((exp, index) => (
<span key={index} className="px-2 py-1 text-xs bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-full drop-shadow bg-gradient-to-r from-gray-200 to-gray-300 dark:from-gray-600 dark:to-gray-700">
{exp}
</span>
))}
</div>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-500 dark:text-gray-400 drop-shadow">{org.projects} projects</span>
<button className="bg-gradient-to-r from-bio-purple to-purple-500 text-white px-4 py-2 rounded-lg hover:from-purple-500 hover:to-purple-600 transition shadow-md hover:shadow-lg drop-shadow">
Contact
</button>
</div>
</div>
</div>
))}
</div>
{filteredOrganizations.length === 0 && (
<div className="text-center py-12 bg-white dark:bg-gray-700 rounded-2xl shadow-lg bg-gradient-to-br from-white to-gray-100 dark:from-gray-700 dark:to-gray-800">
<p className="text-gray-600 dark:text-gray-400 drop-shadow">No organizations found matching your criteria.</p>
<button
className="mt-4 text-bio-purple dark:text-purple-400 hover:underline drop-shadow bg-gradient-to-r from-bio-purple to-purple-500 bg-clip-text text-transparent"
onClick={() => {
setSearchTerm('');
setSectorFilter('All');
}}
>
Clear filters
</button>
</div>
)}
</div>
</div>
);
};
export default DirectoryPage;