diff --git a/pages/api/agents/transport-tracker/analytics.ts b/pages/api/agents/transport-tracker/analytics.ts new file mode 100644 index 0000000..c781e2b --- /dev/null +++ b/pages/api/agents/transport-tracker/analytics.ts @@ -0,0 +1,80 @@ +/** + * API Route: TransportTrackerAgent Analytics + * GET /api/agents/transport-tracker/analytics - Get network stats and user analytics + * GET /api/agents/transport-tracker/analytics?userId=xxx - Get specific user analytics + */ + +import type { NextApiRequest, NextApiResponse } from 'next'; +import { getTransportTrackerAgent } from '../../../../lib/agents/TransportTrackerAgent'; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== 'GET') { + return res.status(405).json({ success: false, error: 'Method not allowed' }); + } + + try { + const agent = getTransportTrackerAgent(); + const { userId } = req.query; + + // If userId provided, return user-specific analytics + if (userId && typeof userId === 'string') { + const userAnalysis = agent.getUserAnalysis(userId); + + if (!userAnalysis) { + return res.status(404).json({ + success: false, + error: `No analytics found for user: ${userId}` + }); + } + + return res.status(200).json({ + success: true, + data: { + user: userAnalysis, + recommendations: userAnalysis.recommendations, + efficiency: { + rating: userAnalysis.efficiency, + carbonPerKm: userAnalysis.carbonPerKm, + totalCarbonKg: userAnalysis.totalCarbonKg + } + } + }); + } + + // Otherwise, return network-wide analytics + const networkStats = agent.getNetworkStats(); + + if (!networkStats) { + return res.status(200).json({ + success: true, + data: { + message: 'No network statistics available yet. Run the agent to collect data.', + networkStats: null + } + }); + } + + res.status(200).json({ + success: true, + data: { + networkStats, + insights: { + avgCarbonPerEvent: networkStats.avgCarbonPerEvent, + avgDistancePerEvent: networkStats.avgDistancePerEvent, + greenTransportPercentage: networkStats.greenTransportPercentage, + topMethods: Object.entries(networkStats.methodDistribution) + .sort(([, a], [, b]) => (b as number) - (a as number)) + .slice(0, 5) + .map(([method, count]) => ({ method, count })) + }, + trends: networkStats.dailyTrends + } + }); + } catch (error: any) { + console.error('Error fetching transport tracker analytics:', error); + res.status(500).json({ success: false, error: error.message || 'Internal server error' }); + } +} diff --git a/pages/api/agents/transport-tracker/index.ts b/pages/api/agents/transport-tracker/index.ts new file mode 100644 index 0000000..3b08209 --- /dev/null +++ b/pages/api/agents/transport-tracker/index.ts @@ -0,0 +1,115 @@ +/** + * API Route: TransportTrackerAgent Management + * GET /api/agents/transport-tracker - Get agent status and metrics + * POST /api/agents/transport-tracker - Control agent (start/stop/run) + */ + +import type { NextApiRequest, NextApiResponse } from 'next'; +import { getTransportTrackerAgent } from '../../../../lib/agents/TransportTrackerAgent'; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const agent = getTransportTrackerAgent(); + + if (req.method === 'GET') { + try { + const metrics = agent.getMetrics(); + const alerts = agent.getAlerts(); + const networkStats = agent.getNetworkStats(); + const patterns = agent.getPatterns(); + + res.status(200).json({ + success: true, + data: { + agent: { + id: agent.config.id, + name: agent.config.name, + description: agent.config.description, + status: agent.status, + priority: agent.config.priority, + intervalMs: agent.config.intervalMs, + enabled: agent.config.enabled + }, + metrics, + alerts: alerts.filter(a => !a.acknowledged).slice(-10), + summary: { + networkStats: networkStats ? { + totalEvents: networkStats.totalEvents, + totalDistanceKm: networkStats.totalDistanceKm, + totalCarbonKg: networkStats.totalCarbonKg, + greenTransportPercentage: networkStats.greenTransportPercentage + } : null, + patternsDetected: patterns.length, + highSeverityPatterns: patterns.filter(p => p.severity === 'high').length + } + } + }); + } catch (error: any) { + console.error('Error getting transport tracker agent status:', error); + res.status(500).json({ success: false, error: error.message || 'Internal server error' }); + } + } else if (req.method === 'POST') { + try { + const { action } = req.body; + + if (!action || typeof action !== 'string') { + return res.status(400).json({ + success: false, + error: 'Action is required (start, stop, run)' + }); + } + + let result: any = {}; + + switch (action) { + case 'start': + await agent.start(); + result = { message: 'Agent started', status: agent.status }; + break; + + case 'stop': + await agent.stop(); + result = { message: 'Agent stopped', status: agent.status }; + break; + + case 'run': + // Run a single execution cycle + const taskResult = await agent.runOnce(); + result = { + message: 'Agent cycle completed', + status: agent.status, + taskResult + }; + break; + + case 'pause': + agent.pause(); + result = { message: 'Agent paused', status: agent.status }; + break; + + case 'resume': + agent.resume(); + result = { message: 'Agent resumed', status: agent.status }; + break; + + default: + return res.status(400).json({ + success: false, + error: `Unknown action: ${action}. Valid actions: start, stop, run, pause, resume` + }); + } + + res.status(200).json({ + success: true, + data: result + }); + } catch (error: any) { + console.error('Error controlling transport tracker agent:', error); + res.status(500).json({ success: false, error: error.message || 'Internal server error' }); + } + } else { + res.status(405).json({ success: false, error: 'Method not allowed' }); + } +} diff --git a/pages/api/agents/transport-tracker/patterns.ts b/pages/api/agents/transport-tracker/patterns.ts new file mode 100644 index 0000000..1c508af --- /dev/null +++ b/pages/api/agents/transport-tracker/patterns.ts @@ -0,0 +1,110 @@ +/** + * API Route: TransportTrackerAgent Pattern Detection + * GET /api/agents/transport-tracker/patterns - Get detected inefficiency patterns + */ + +import type { NextApiRequest, NextApiResponse } from 'next'; +import { getTransportTrackerAgent } from '../../../../lib/agents/TransportTrackerAgent'; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== 'GET') { + return res.status(405).json({ success: false, error: 'Method not allowed' }); + } + + try { + const agent = getTransportTrackerAgent(); + const { severity, type } = req.query; + + let patterns = agent.getPatterns(); + + // Filter by severity if provided + if (severity && typeof severity === 'string') { + const validSeverities = ['low', 'medium', 'high']; + if (!validSeverities.includes(severity)) { + return res.status(400).json({ + success: false, + error: `Invalid severity. Valid values: ${validSeverities.join(', ')}` + }); + } + patterns = patterns.filter(p => p.severity === severity); + } + + // Filter by type if provided + if (type && typeof type === 'string') { + const validTypes = ['inefficient_route', 'high_carbon', 'excessive_handling', 'cold_chain_break']; + if (!validTypes.includes(type)) { + return res.status(400).json({ + success: false, + error: `Invalid type. Valid values: ${validTypes.join(', ')}` + }); + } + patterns = patterns.filter(p => p.type === type); + } + + // Calculate summary statistics + const summary = { + total: patterns.length, + bySeverity: { + high: patterns.filter(p => p.severity === 'high').length, + medium: patterns.filter(p => p.severity === 'medium').length, + low: patterns.filter(p => p.severity === 'low').length + }, + byType: { + inefficient_route: patterns.filter(p => p.type === 'inefficient_route').length, + high_carbon: patterns.filter(p => p.type === 'high_carbon').length, + excessive_handling: patterns.filter(p => p.type === 'excessive_handling').length, + cold_chain_break: patterns.filter(p => p.type === 'cold_chain_break').length + }, + totalPotentialSavingsKg: patterns.reduce((sum, p) => sum + p.potentialSavingsKg, 0) + }; + + res.status(200).json({ + success: true, + data: { + patterns, + summary, + recommendations: generatePatternRecommendations(patterns) + } + }); + } catch (error: any) { + console.error('Error fetching transport patterns:', error); + res.status(500).json({ success: false, error: error.message || 'Internal server error' }); + } +} + +/** + * Generate actionable recommendations based on detected patterns + */ +function generatePatternRecommendations(patterns: any[]): string[] { + const recommendations: string[] = []; + + const highCarbonCount = patterns.filter(p => p.type === 'high_carbon').length; + const excessiveHandlingCount = patterns.filter(p => p.type === 'excessive_handling').length; + const coldChainBreaks = patterns.filter(p => p.type === 'cold_chain_break').length; + + if (highCarbonCount > 3) { + recommendations.push('Multiple high-carbon transport events detected. Consider implementing a fleet electrification program or partnering with eco-friendly logistics providers.'); + } + + if (excessiveHandlingCount > 2) { + recommendations.push('Products are being handled too many times. Review supply chain to consolidate shipments and reduce touchpoints.'); + } + + if (coldChainBreaks > 0) { + recommendations.push('Cold chain integrity issues detected. Implement temperature monitoring IoT devices and establish clear handoff protocols.'); + } + + const totalSavings = patterns.reduce((sum, p) => sum + p.potentialSavingsKg, 0); + if (totalSavings > 100) { + recommendations.push(`Addressing detected patterns could save approximately ${totalSavings.toFixed(1)} kg of CO2 emissions.`); + } + + if (recommendations.length === 0) { + recommendations.push('Transport patterns look healthy. Continue monitoring for optimization opportunities.'); + } + + return recommendations; +} diff --git a/pages/api/agents/transport-tracker/savings.ts b/pages/api/agents/transport-tracker/savings.ts new file mode 100644 index 0000000..78b1303 --- /dev/null +++ b/pages/api/agents/transport-tracker/savings.ts @@ -0,0 +1,89 @@ +/** + * API Route: TransportTrackerAgent Savings Calculator + * GET /api/agents/transport-tracker/savings - Calculate carbon savings vs conventional logistics + */ + +import type { NextApiRequest, NextApiResponse } from 'next'; +import { getTransportTrackerAgent } from '../../../../lib/agents/TransportTrackerAgent'; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== 'GET') { + return res.status(405).json({ success: false, error: 'Method not allowed' }); + } + + try { + const agent = getTransportTrackerAgent(); + const savings = agent.calculateSavingsVsConventional(); + const networkStats = agent.getNetworkStats(); + + if (!networkStats || networkStats.totalEvents === 0) { + return res.status(200).json({ + success: true, + data: { + message: 'No transport data available for savings calculation.', + savings: null + } + }); + } + + // Calculate additional context metrics + const context = { + // Equivalent miles driven by average car (0.404 kg CO2 per mile) + equivalentCarMiles: Math.round(savings.savedKg / 0.404), + + // Equivalent trees needed to absorb this CO2 (21 kg per tree per year) + equivalentTreeYears: Math.round((savings.savedKg / 21) * 10) / 10, + + // Equivalent gallons of gasoline (8.887 kg CO2 per gallon) + equivalentGallonsSaved: Math.round((savings.savedKg / 8.887) * 10) / 10, + + // Equivalent smartphone charges (0.0085 kg CO2 per charge) + equivalentPhoneCharges: Math.round(savings.savedKg / 0.0085) + }; + + // Generate insights based on performance + const insights: string[] = []; + + if (savings.savedPercentage >= 90) { + insights.push('Exceptional performance! Your local supply chain is operating at near-optimal carbon efficiency.'); + } else if (savings.savedPercentage >= 70) { + insights.push('Great job! Your local sourcing strategy is significantly reducing carbon emissions.'); + } else if (savings.savedPercentage >= 50) { + insights.push('Good progress. There are still opportunities to further reduce your carbon footprint.'); + } else if (savings.savedPercentage > 0) { + insights.push('Your local network is making a positive impact. Consider expanding local sourcing to improve further.'); + } + + if (networkStats.greenTransportPercentage >= 50) { + insights.push(`${networkStats.greenTransportPercentage}% of your transport uses green methods - excellent sustainability focus!`); + } + + res.status(200).json({ + success: true, + data: { + savings: { + localGreenCarbonKg: savings.localGreenCarbon, + conventionalCarbonKg: savings.conventionalCarbon, + savedKg: savings.savedKg, + savedPercentage: savings.savedPercentage + }, + context, + insights, + methodology: { + description: 'Savings calculated by comparing LocalGreenChain transport to conventional supply chain assumptions.', + assumptions: [ + 'Conventional: Average 1,500 miles transport per item at 0.2 kg CO2/mile', + 'LocalGreenChain: Actual tracked distances and transport methods', + 'Green methods include: walking, bicycle, electric vehicles, rail' + ] + } + } + }); + } catch (error: any) { + console.error('Error calculating transport savings:', error); + res.status(500).json({ success: false, error: error.message || 'Internal server error' }); + } +}