diff --git a/frontend/src/pages/History/History.js b/frontend/src/pages/History/History.js index 186cf15..b78c41c 100644 --- a/frontend/src/pages/History/History.js +++ b/frontend/src/pages/History/History.js @@ -31,7 +31,7 @@ const History = () => { 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 [applicationTypeFilter, setApplicationTypeFilter] = useState('all'); // all, granular, liquid, mowing const [showFilters, setShowFilters] = useState(false); const [showProductDropdown, setShowProductDropdown] = useState(false); @@ -252,7 +252,8 @@ const History = () => { } // Application type filter - if (applicationTypeFilter !== 'all' && getApplicationType(app) !== applicationTypeFilter) return false; + if (applicationTypeFilter === 'mowing') return false; // hide app items when filtering for mowing only + if (applicationTypeFilter !== 'all' && applicationTypeFilter !== 'mowing' && getApplicationType(app) !== applicationTypeFilter) return false; return true; }); @@ -330,6 +331,43 @@ const History = () => { const totalMowingSessions = sortedMowingLogs.length; + // Build unified history list (applications + mowing) sorted by date + const unifiedHistoryItems = (() => { + // Application items + const apps = [...filteredApplications].map((application) => { + const log = applicationLogs.find((l) => l.planId === application.id); + const dateStr = log?.applicationDate || application.plannedDate || application.updatedAt || application.createdAt; + const date = dateStr ? new Date(dateStr) : new Date(0); + return { kind: 'application', date, application, log }; + }); + // Mowing items (already date-filtered and property-filtered above) + const mows = [...sortedMowingLogs].map((log) => { + const dateStr = log.session_date || log.created_at; + const date = dateStr ? new Date(dateStr) : new Date(0); + return { kind: 'mowing', date, log }; + }); + + let items = [...apps, ...mows]; + + // Status filter: mowing sessions are treated as completed + if (statusFilter === 'archived') { + items = items.filter((it) => it.kind === 'application' && it.application.status === 'archived'); + } else if (statusFilter === 'completed') { + items = items.filter((it) => (it.kind === 'application' && it.application.status === 'completed') || it.kind === 'mowing'); + } + + // Application Type filter across unified list + if (applicationTypeFilter === 'granular' || applicationTypeFilter === 'liquid') { + items = items.filter((it) => it.kind === 'application' && getApplicationType(it.application) === applicationTypeFilter); + } else if (applicationTypeFilter === 'mowing') { + items = items.filter((it) => it.kind === 'mowing'); + } + + // Sort newest first + items.sort((a, b) => b.date - a.date); + return items; + })(); + if (loading) { return (
@@ -557,6 +595,7 @@ const History = () => { +
@@ -598,168 +637,145 @@ const History = () => { )} - {/* Applications List */} - {sortedApplications.length === 0 ? ( + {/* Unified History List */} + {unifiedHistoryItems.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.'} -

+

No history items

+

Try adjusting your filters or date range.

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

- {application.propertyName} - {application.sectionNames} -

- - {application.status === 'archived' ? 'Archived' : 'Completed'} - -
+ {unifiedHistoryItems.map((item) => { + if (item.kind === 'application') { + const application = item.application; + const log = item.log; + 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 + + {new Date(item.date).toLocaleString()}
+
+ + {application.propertyName} +
+
+ + {application.equipmentName} +
+
+ + {/* GPS Tracking Stats */} + {log?.gpsTrack && ( +
+
+
Duration
+
+ {Math.round((log.gpsTrack?.duration || 0) / 60)} min +
+
+
+
GPS Points
+
+ {log.gpsTrack?.points?.length || 0} +
+
+
+
Distance
+
+ {Math.round(log.gpsTrack?.totalDistance || 0)} ft +
+
+
+
Coverage
+
+ {calculateCoverage(application, log)}% +
+
+
+ )} + + {/* Products */} + {application.productDetails && application.productDetails.length > 0 && ( +
+
+ + Products Applied: +
+
+ {application.productDetails.map((product, index) => ( + + {product.name} ({product.rateAmount} {product.rateUnit}) + + ))} +
+
+ )} + + {application.notes && ( +

+ Notes: {application.notes} +

)}
- {/* 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.productDetails && application.productDetails.length > 0 && ( -
-
- - Products Applied: -
-
- {application.productDetails.map((product, index) => ( - - {product.name} ({product.rateAmount} {product.rateUnit}) - - ))} -
-
- )} - - {application.notes && ( -

- Notes: {application.notes} -

- )} -
- -
- +
+ +
-
- ); - })} -
- )} + ); + } - {/* Mowing History */} - {mowingLogs.length > 0 && ( -
-
-

Mowing History

-

{totalMowingSessions} session{totalMowingSessions!==1?'s':''}

-
-
- {sortedMowingLogs.map((log) => { - const durationMin = Math.round((log.duration_seconds || log.durationSeconds || log.gpsTrack?.duration || 0) / 60); - const avg = (log.average_speed_mph || log.averageSpeed || 0).toFixed?.(1) || Number(log.averageSpeed || 0).toFixed(1); - const distFeet = Math.round(((log.total_distance_meters || log.gpsTrack?.totalDistance || 0) * 3.28084) || 0); - return ( -
+ // Mowing entry + const log = item.log; + const durationMin = Math.round((log.duration_seconds || log.durationSeconds || log.gpsTrack?.duration || 0) / 60); + const avg = (log.average_speed_mph || log.averageSpeed || 0).toFixed?.(1) || Number(log.averageSpeed || 0).toFixed(1); + const distFeet = Math.round(((log.total_distance_meters || log.gpsTrack?.totalDistance || 0) * 3.28084) || 0); + return ( +
+
{log.property_name || 'Property'}
-
{log.equipment_name || 'Mower'} • {avg} mph • {distFeet} ft • {durationMin} min
+
{new Date(item.date).toLocaleString()} • {log.equipment_name || 'Mower'} • {avg} mph • {distFeet} ft • {durationMin} min
- ); - })} -
+
+ ); + })}
)} diff --git a/frontend/src/pages/Mowing/Mowing.js b/frontend/src/pages/Mowing/Mowing.js index 657e0c6..5f5e3d5 100644 --- a/frontend/src/pages/Mowing/Mowing.js +++ b/frontend/src/pages/Mowing/Mowing.js @@ -75,6 +75,29 @@ const Mowing = () => { {p.notes && (

"{p.notes}"

)} + + {/* Recent sessions for this property */} + {sessions.filter(s => s.property_id === p.property_id).length > 0 && ( +
+

Recent Sessions

+
+ {sessions.filter(s => s.property_id === p.property_id).slice(0,3).map(s => ( +
+
+ {new Date(s.created_at || s.session_date).toLocaleString()} • {(s.average_speed_mph || s.averageSpeed || 0).toFixed?.(1) || Number(s.averageSpeed || 0).toFixed(1)} mph • {Math.round(((s.total_distance_meters || s.gpsTrack?.totalDistance || 0) * 3.28084) || 0)} ft +
+ +
+ ))} +
+
+ )}

{p.planned_date ? new Date(p.planned_date).toLocaleDateString() : 'No date'}

@@ -125,8 +148,14 @@ const Mowing = () => {
{Number(s.cut_height_inches || 0).toFixed(2)}"
{(s.average_speed_mph || s.averageSpeed || 0).toFixed?.(1) || Number(s.averageSpeed || 0).toFixed(1)} mph
{Math.round(((s.total_distance_meters || s.gpsTrack?.totalDistance || 0) * 3.28084) || 0)} ft
-
- +
+
))}