import { useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Briefcase, Filter, Grid3X3, List, Plus } from 'lucide-react'; import { MainLayout } from '@/components/layout/MainLayout.tsx'; import PageHeader from '@/components/layout/PageHeader.tsx'; import OrganizationCard from '@/components/organization/OrganizationCard.tsx'; import Badge from '@/components/ui/Badge.tsx'; import Button from '@/components/ui/Button.tsx'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card.tsx'; import { Container, Flex, Grid, Stack } from '@/components/ui/layout'; import SearchInput from '@/components/ui/SearchInput.tsx'; import Select from '@/components/ui/Select.tsx'; import Spinner from '@/components/ui/Spinner.tsx'; import { useOrganizations } from '@/hooks/api/useOrganizationsAPI.ts'; import { useTranslation } from '@/hooks/useI18n.tsx'; import { useNavigation } from '@/hooks/useNavigation.tsx'; import { getTranslatedSectorName } from '@/lib/sector-mapper.ts'; import { getOrganizationSubtypeLabel } from '@/schemas/organizationSubtype.ts'; import type { Organization } from '@/types.ts'; const OrganizationsListPage = () => { const { t } = useTranslation(); const navigate = useNavigate(); const { handleBackNavigation, handleFooterNavigate } = useNavigation(); const { data: organizations, isLoading, error } = useOrganizations(); // Filter and search state const [searchQuery, setSearchQuery] = useState(''); const [selectedSector, setSelectedSector] = useState('all'); const [selectedSubtype, setSelectedSubtype] = useState('all'); const [sortBy, setSortBy] = useState('name'); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [showVerifiedOnly, setShowVerifiedOnly] = useState(false); // Process organizations data const processedOrganizations = useMemo(() => { if (!organizations) return []; const filtered = organizations.filter((org: Organization) => { // Search filter const matchesSearch = !searchQuery || org.Name?.toLowerCase().includes(searchQuery.toLowerCase()) || org.Description?.toLowerCase().includes(searchQuery.toLowerCase()) || org.Address?.toLowerCase().includes(searchQuery.toLowerCase()); // Sector filter const matchesSector = selectedSector === 'all' || org.Sector === selectedSector; // Subtype filter const matchesSubtype = selectedSubtype === 'all' || org.Subtype === selectedSubtype; // Verified filter const matchesVerified = !showVerifiedOnly || org.Verified; return matchesSearch && matchesSector && matchesSubtype && matchesVerified; }); // Sort filtered.sort((a: Organization, b: Organization) => { switch (sortBy) { case 'name': return a.Name?.localeCompare(b.Name || '') || 0; case 'sector': return (a.Sector || '').localeCompare(b.Sector || ''); case 'verified': return (b.Verified ? 1 : 0) - (a.Verified ? 1 : 0); case 'activity': // Sort by number of resource flows + matches const aActivity = (a.ResourceFlows?.length || 0) + (a.Matches?.length || 0); const bActivity = (b.ResourceFlows?.length || 0) + (b.Matches?.length || 0); return bActivity - aActivity; default: return 0; } }); return filtered; }, [organizations, searchQuery, selectedSector, selectedSubtype, sortBy, showVerifiedOnly]); // Get unique sectors and subtypes for filters const filterOptions = useMemo(() => { if (!organizations) return { sectors: [], subtypes: [] }; const sectors = Array.from( new Set(organizations.map((org: Organization) => org.Sector).filter(Boolean)) ); const subtypes = Array.from( new Set(organizations.map((org: Organization) => org.Subtype).filter(Boolean)) ); return { sectors, subtypes }; }, [organizations]); // Sector options for dropdown const sectorOptions = [ { value: 'all', label: t('organizationsList.allSectors') }, ...filterOptions.sectors.map((sector) => ({ value: sector, label: getTranslatedSectorName(sector), })), ]; // Subtype options for dropdown const subtypeOptions = [ { value: 'all', label: t('organizationsList.allSubtypes') }, ...filterOptions.subtypes.map((subtype) => ({ value: subtype, label: getOrganizationSubtypeLabel(subtype), })), ]; // Sort options const sortOptions = [ { value: 'name', label: t('organizationsList.sortByName') }, { value: 'sector', label: t('organizationsList.sortBySector') }, { value: 'verified', label: t('organizationsList.sortByVerified') }, { value: 'activity', label: t('organizationsList.sortByActivity') }, ]; const handleCreateOrganization = () => { navigate('/organizations/new'); }; const handleOrganizationClick = (organization: Organization) => { navigate(`/organization/${organization.ID}`); }; const clearFilters = () => { setSearchQuery(''); setSelectedSector('all'); setSelectedSubtype('all'); setShowVerifiedOnly(false); setSortBy('name'); }; const activeFiltersCount = [ searchQuery, selectedSector !== 'all', selectedSubtype !== 'all', showVerifiedOnly, ].filter(Boolean).length; if (isLoading) { return (

{t('organizationsList.loading')}

); } if (error) { return (

{error.message}

); } return ( {/* Header Actions */}
{activeFiltersCount > 0 && ( )}
{/* Search and Filters */} {t('organizationsList.searchAndFilters')}
setShowVerifiedOnly(e.target.checked)} className="rounded" /> {t('organizationsList.verifiedOnly')}
{/* Results Summary */}

{t('organizationsList.showingResults', { shown: processedOrganizations.length, total: organizations?.length || 0, })}

{activeFiltersCount > 0 && ( {activeFiltersCount} {t('organizationsList.filtersActive')} )}
{/* Organizations Grid/List */} {processedOrganizations.length > 0 ? ( {processedOrganizations.map((organization: Organization) => ( ))} ) : (

{t('organizationsList.noOrganizations')}

{searchQuery || selectedSector !== 'all' || selectedSubtype !== 'all' || showVerifiedOnly ? t('organizationsList.noOrganizationsMatchFilters') : t('organizationsList.noOrganizationsYet')}

{(searchQuery || selectedSector !== 'all' || selectedSubtype !== 'all' || showVerifiedOnly) && ( )} {!searchQuery && selectedSector === 'all' && selectedSubtype === 'all' && !showVerifiedOnly && ( )}
)}
); }; export default OrganizationsListPage;