import { useMemo } from 'react'; import { ArrowUp, BarChart3, Clock, Target, TrendingUp, Users, Building2 } from 'lucide-react'; import { MainLayout } from '@/components/layout/MainLayout.tsx'; import PageHeader from '@/components/layout/PageHeader.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 MetricItem from '@/components/ui/MetricItem.tsx'; import Spinner from '@/components/ui/Spinner.tsx'; import { Container, Flex, Grid, Stack } from '@/components/ui/layout'; import { useConnectionStatistics, useImpactMetrics, useMatchingStatistics, usePlatformStatistics, useResourceFlowStatistics, useSupplyDemandAnalysis, } from '@/hooks/api/useAnalyticsAPI.ts'; import { useTranslation } from '@/hooks/useI18n.tsx'; import { useNavigation } from '@/hooks/useNavigation.tsx'; const AnalyticsDashboard = () => { const { t } = useTranslation(); const { handleBackNavigation, handleFooterNavigate } = useNavigation(); // Analytics data const { data: platformStats, isLoading: isLoadingPlatform } = usePlatformStatistics(); const { data: matchingStats, isLoading: isLoadingMatching } = useMatchingStatistics(); const { data: resourceFlowStats, isLoading: isLoadingResourceFlow } = useResourceFlowStatistics(); const { data: impactMetrics, isLoading: isLoadingImpact } = useImpactMetrics(); const { data: connectionStats, isLoading: isLoadingConnections } = useConnectionStatistics(); const { data: supplyDemand, isLoading: isLoadingSupplyDemand } = useSupplyDemandAnalysis(); const isLoading = isLoadingPlatform || isLoadingMatching || isLoadingResourceFlow || isLoadingImpact || isLoadingConnections || isLoadingSupplyDemand; // Calculate derived metrics const analytics = useMemo(() => { const platform = platformStats || {}; const matching = matchingStats || {}; const resourceFlow = resourceFlowStats || {}; const impact = impactMetrics || {}; const connections = connectionStats || {}; const supplyDemandData = supplyDemand || {}; return { // Platform overview totalOrganizations: platform.total_organizations || 0, totalSites: platform.total_sites || 0, totalResourceFlows: platform.total_resource_flows || 0, totalMatches: platform.total_matches || 0, // Matching performance matchSuccessRate: matching.match_success_rate || 0, avgMatchTime: matching.avg_match_time_days || 0, totalMatchValue: matching.total_match_value || 0, topResourceTypes: matching.top_resource_types || [], matchTrends: matching.match_trends || [], // Resource flow analytics flowsByType: resourceFlow.flows_by_type || {}, flowsBySector: resourceFlow.flows_by_sector || {}, avgFlowValue: resourceFlow.avg_flow_value || 0, totalFlowVolume: resourceFlow.total_flow_volume || 0, // Impact metrics totalCo2Saved: impact.total_co2_saved_tonnes || 0, totalEconomicValue: impact.total_economic_value || 0, activeMatchesCount: impact.active_matches_count || 0, environmentalBreakdown: impact.environmental_breakdown || {}, // Connection analytics totalConnections: connections.total_connections || 0, activeConnections: connections.active_connections || 0, potentialConnections: connections.potential_connections || 0, connectionRate: connections.connection_rate || 0, // Supply/demand topNeeds: supplyDemandData.top_needs || [], topOffers: supplyDemandData.top_offers || [], marketGaps: supplyDemandData.market_gaps || [], }; }, [platformStats, matchingStats, resourceFlowStats, impactMetrics, connectionStats, supplyDemand]); const formatCurrency = (value: number) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(value); }; const formatNumber = (value: number) => { return new Intl.NumberFormat('en-US').format(value); }; const formatPercentage = (value: number) => { return `${(value * 100).toFixed(1)}%`; }; // Simple bar chart component using CSS const SimpleBarChart = ({ data, title }: { data: Array<{ label: string; value: number; color?: string }>; title: string }) => { const maxValue = Math.max(...data.map(d => d.value)); return (

{title}

{data.map((item, index) => (
{item.label}
{typeof item.value === 'number' && item.value < 1 ? formatPercentage(item.value) : formatNumber(item.value) }
))}
); }; return ( {/* Key Performance Indicators */} } label={t('analyticsDashboard.totalOrganizations', 'Total Organizations')} value={formatNumber(analytics.totalOrganizations)} /> } label={t('analyticsDashboard.totalResourceFlows', 'Resource Flows')} value={formatNumber(analytics.totalResourceFlows)} /> } label={t('analyticsDashboard.totalMatches', 'Total Matches')} value={formatNumber(analytics.totalMatches)} /> } label={t('analyticsDashboard.co2Saved', 'CO₂ Saved (t)')} value={formatNumber(analytics.totalCo2Saved)} /> {/* Platform Overview */} {/* Matching Performance */} {t('analyticsDashboard.matchingPerformance', 'Matching Performance')} {isLoading ? (
) : (
{formatPercentage(analytics.matchSuccessRate)}

{t('analyticsDashboard.successRate', 'Success Rate')}

{analytics.avgMatchTime.toFixed(1)}

{t('analyticsDashboard.avgDays', 'Avg Days')}

{t('analyticsDashboard.totalMatchValue', 'Total Match Value')}
{formatCurrency(analytics.totalMatchValue)}
{analytics.topResourceTypes.length > 0 && ( ({ label: item.type, value: item.count, }))} title={t('analyticsDashboard.topResourceTypes', 'Top Resource Types')} /> )}
)}
{/* Connection Analytics */} {t('analyticsDashboard.connectionAnalytics', 'Connection Analytics')} {isLoading ? (
) : (
{formatNumber(analytics.totalConnections)}

{t('analyticsDashboard.total', 'Total')}

{formatNumber(analytics.activeConnections)}

{t('analyticsDashboard.active', 'Active')}

{formatNumber(analytics.potentialConnections)}

{t('analyticsDashboard.potential', 'Potential')}

{t('analyticsDashboard.connectionRate', 'Connection Rate')}
{formatPercentage(analytics.connectionRate)}
)}
{/* Resource Flow Distribution */} {t('analyticsDashboard.resourceFlowDistribution', 'Resource Flow Distribution')} {isLoading ? (
) : (
{t('analyticsDashboard.totalFlowVolume', 'Total Flow Volume')}
{formatNumber(analytics.totalFlowVolume)}
{t('analyticsDashboard.avgFlowValue', 'Average Flow Value')}
{formatCurrency(analytics.avgFlowValue)}
{Object.keys(analytics.flowsByType).length > 0 && ( ({ label: type, value: count as number, }))} title={t('analyticsDashboard.flowsByType', 'Flows by Type')} /> )}
)}
{/* Environmental Impact */} {t('analyticsDashboard.environmentalImpact', 'Environmental Impact')} {isLoading ? (
) : (
{formatNumber(analytics.totalCo2Saved)}

{t('analyticsDashboard.tonnesCo2Saved', 'Tonnes CO₂ Saved')}

{t('analyticsDashboard.perYear', 'per year')}
{formatCurrency(analytics.totalEconomicValue)}

{t('analyticsDashboard.economicValueCreated', 'Economic Value Created')}

{t('analyticsDashboard.annual', 'annual')}
{formatNumber(analytics.activeMatchesCount)}

{t('analyticsDashboard.activeMatches', 'Active Matches')}

{t('analyticsDashboard.operational', 'operational')}
{Object.keys(analytics.environmentalBreakdown).length}

{t('analyticsDashboard.impactCategories', 'Impact Categories')}

{t('analyticsDashboard.tracked', 'tracked')}
)}
{/* Supply & Demand Analysis */} {/* Top Needs */} {t('analyticsDashboard.topNeeds', 'Top Resource Needs')} {isLoading ? (
) : analytics.topNeeds.length > 0 ? (
{analytics.topNeeds.slice(0, 8).map((need: any, index: number) => (
{index + 1}

{need.item}

{need.sector}

{need.count} {t('analyticsDashboard.requests', 'requests')}
))}
) : (

{t('analyticsDashboard.noNeedsData', 'No needs data available')}

)}
{/* Top Offers */} {t('analyticsDashboard.topOffers', 'Top Resource Offers')} {isLoading ? (
) : analytics.topOffers.length > 0 ? (
{analytics.topOffers.slice(0, 8).map((offer: any, index: number) => (
{index + 1}

{offer.item}

{offer.sector}

{offer.count} {t('analyticsDashboard.offers', 'offers')}
))}
) : (

{t('analyticsDashboard.noOffersData', 'No offers data available')}

)}
{/* Market Gaps */} {analytics.marketGaps.length > 0 && ( {t('analyticsDashboard.marketGaps', 'Market Gaps & Opportunities')}
{analytics.marketGaps.slice(0, 6).map((gap: any, index: number) => (
{gap.resource_type}

{gap.description}

{gap.severity} {t('analyticsDashboard.gap', 'gap')} {gap.potential_matches} {t('analyticsDashboard.potentialMatches', 'potential matches')}
))}
)} {/* Export Actions */} {t('analyticsDashboard.exportData', 'Export Analytics Data')}
); }; export default AnalyticsDashboard;