import React, { useState, useEffect } from 'react'; import { ClockIcon, MapPinIcon, WrenchScrewdriverIcon, BeakerIcon, EyeIcon, CalendarIcon, ChartBarIcon } from '@heroicons/react/24/outline'; import { applicationsAPI } from '../../services/api'; import LoadingSpinner from '../../components/UI/LoadingSpinner'; import ApplicationViewModal from '../../components/Applications/ApplicationViewModal'; import toast from 'react-hot-toast'; const History = () => { const [completedApplications, setCompletedApplications] = useState([]); const [applicationLogs, setApplicationLogs] = useState([]); const [loading, setLoading] = useState(true); const [showViewModal, setShowViewModal] = useState(false); const [viewingApplication, setViewingApplication] = useState(null); const [selectedPropertyDetails, setSelectedPropertyDetails] = useState(null); const [dateFilter, setDateFilter] = useState('all'); // all, today, week, month, custom const [dateRangeStart, setDateRangeStart] = useState(''); const [dateRangeEnd, setDateRangeEnd] = useState(''); const [sortBy, setSortBy] = useState('date'); // date, area, duration const [statusFilter, setStatusFilter] = useState('all'); // all, completed, archived const [propertyFilter, setPropertyFilter] = useState('all'); const [selectedProducts, setSelectedProducts] = useState([]); const [applicationTypeFilter, setApplicationTypeFilter] = useState('all'); // all, granular, liquid const [showFilters, setShowFilters] = useState(false); const [showProductDropdown, setShowProductDropdown] = useState(false); // Calculate coverage percentage based on GPS tracking and equipment specifications const calculateCoverage = (application, log) => { if (!log?.gpsTrack?.points || log.gpsTrack.points.length < 2) { return 0; // No movement = no coverage } const totalDistance = log.gpsTrack.totalDistance || 0; const plannedArea = application.totalSectionArea || 0; if (totalDistance === 0 || plannedArea === 0) { return 0; } // Estimate equipment width based on type let equipmentWidth = 4; // Default 4 feet for unknown equipment const equipmentName = application.equipmentName?.toLowerCase() || ''; if (equipmentName.includes('spreader')) { equipmentWidth = 12; // Spreaders typically 8-16 feet wide } else if (equipmentName.includes('sprayer')) { equipmentWidth = 20; // Sprayers typically 15-30 feet wide } else if (equipmentName.includes('mower')) { equipmentWidth = 6; // Mowers typically 4-8 feet wide } // Calculate theoretical coverage area // Distance (feet) * Width (feet) = Coverage area (sq ft) const theoreticalCoverageArea = totalDistance * equipmentWidth; // Calculate coverage percentage (capped at 100%) const coveragePercentage = Math.min((theoreticalCoverageArea / plannedArea) * 100, 100); return Math.round(coveragePercentage); }; useEffect(() => { fetchHistoryData(); }, []); // Close dropdown when clicking outside useEffect(() => { const handleClickOutside = (event) => { if (showProductDropdown && !event.target.closest('.relative')) { setShowProductDropdown(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [showProductDropdown]); const fetchHistoryData = async () => { try { setLoading(true); // Fetch completed and archived applications const [completedResponse, archivedResponse] = await Promise.all([ applicationsAPI.getPlans({ status: 'completed' }), applicationsAPI.getPlans({ status: 'archived' }) ]); const completedPlans = completedResponse.data.data.plans || []; const archivedPlans = archivedResponse.data.data.plans || []; const allHistoryApplications = [...completedPlans, ...archivedPlans]; // Fetch application logs for additional details const logsResponse = await applicationsAPI.getLogs(); const logs = logsResponse.data.data.logs || []; setCompletedApplications(allHistoryApplications); setApplicationLogs(logs); } catch (error) { console.error('Failed to fetch history data:', error); toast.error('Failed to load application history'); } finally { setLoading(false); } }; const handleViewApplication = async (application) => { try { setViewingApplication(application); setShowViewModal(true); } catch (error) { console.error('Failed to load application details:', error); toast.error('Failed to load application details'); } }; // Get unique values for filter options const uniqueProperties = [...new Set(completedApplications.map(app => app.propertyName))].filter(Boolean); const uniqueProducts = [...new Set( completedApplications.flatMap(app => app.products ? app.products.map(p => p.productName) : [] ) )].filter(Boolean); // Filter applications based on all filters const filteredApplications = completedApplications.filter(app => { // Date filter if (dateFilter !== 'all') { const appDate = new Date(app.plannedDate); const now = new Date(); switch (dateFilter) { case 'today': if (appDate.toDateString() !== now.toDateString()) return false; break; case 'week': const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); if (appDate < weekAgo) return false; break; case 'month': const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); if (appDate < monthAgo) return false; break; } } // Status filter if (statusFilter !== 'all' && app.status !== statusFilter) return false; // Property filter if (propertyFilter !== 'all' && app.propertyName !== propertyFilter) return false; // Product filter if (productFilter !== 'all') { if (!app.products || !app.products.some(p => p.productName === productFilter)) return false; } return true; }); // Sort applications const sortedApplications = [...filteredApplications].sort((a, b) => { switch (sortBy) { case 'date': return new Date(b.plannedDate) - new Date(a.plannedDate); case 'area': return (b.totalSectionArea || 0) - (a.totalSectionArea || 0); case 'duration': const logA = applicationLogs.find(log => log.planId === a.id); const logB = applicationLogs.find(log => log.planId === b.id); return (logB?.gpsTrack?.duration || 0) - (logA?.gpsTrack?.duration || 0); default: return 0; } }); // Calculate summary statistics const totalApplications = completedApplications.length; const totalAreaTreated = completedApplications.reduce((sum, app) => sum + (app.totalSectionArea || 0), 0); const totalDuration = applicationLogs.reduce((sum, log) => sum + (log.gpsTrack?.duration || 0), 0); if (loading) { return (
); } return (

Application History

{/* Summary Statistics */}

Total Applications

{totalApplications}

Total Area Treated

{Math.round(totalAreaTreated / 1000)}k sq ft

Total Time

{Math.round(totalDuration / 3600)}h

{/* Filters and Sort */}
{showFilters && (
{/* Date Range Inputs */} {dateFilter === 'custom' && (
setDateRangeStart(e.target.value)} className="w-full border border-gray-300 rounded px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" />
setDateRangeEnd(e.target.value)} className="w-full border border-gray-300 rounded px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" />
)}
{showProductDropdown && (
{uniqueProducts.length === 0 ? (
No products available
) : ( <>
{uniqueProducts.map(product => ( ))} )}
)}
)}
{/* Applications List */} {sortedApplications.length === 0 ? (

{statusFilter === 'completed' ? 'No completed applications' : statusFilter === 'archived' ? 'No archived applications' : 'No completed or archived applications'}

{statusFilter === 'completed' ? 'Complete some applications to see them here.' : statusFilter === 'archived' ? 'Archive some completed applications to see them here.' : 'Complete and archive applications to see them here.'}

) : (
{sortedApplications.map((application) => { const log = applicationLogs.find(log => log.planId === application.id); return (

{application.propertyName} - {application.sectionNames}

{application.status === 'archived' ? 'Archived' : 'Completed'}
{new Date(application.plannedDate).toLocaleDateString()}
{(application.totalSectionArea || 0).toLocaleString()} sq ft
{application.equipmentName}
{log && (
{Math.round((log.gpsTrack?.duration || 0) / 60)} min
)}
{/* GPS Tracking Stats */} {log && (
Avg Speed
{log.averageSpeed?.toFixed(1) || 0} mph
GPS Points
{log.gpsTrack?.points?.length || 0}
Distance
{Math.round(log.gpsTrack?.totalDistance || 0)} ft
Coverage
{calculateCoverage(application, log)}%
)} {/* Products */} {application.products && application.products.length > 0 && (
Products Applied:
{application.products.map((product, index) => ( {product.productName} ({product.rateAmount} {product.rateUnit}) ))}
)} {application.notes && (

Notes: {application.notes}

)}
); })}
)} {/* Application View Modal */} {showViewModal && viewingApplication && ( { setShowViewModal(false); setViewingApplication(null); }} /> )}
); }; export default History;