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 = () => {
All Types
Granular
Liquid
+ Mowing
@@ -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}
-
- )}
-
-
-
-
handleViewApplication(application)}
- className="p-2 text-indigo-600 hover:text-indigo-800 hover:bg-indigo-50 rounded"
- title="View details"
- >
-
-
+
+ handleViewApplication(application)}
+ className="p-2 text-indigo-600 hover:text-indigo-800 hover:bg-indigo-50 rounded"
+ title="View details"
+ >
+
+
+
-
- );
- })}
-
- )}
+ );
+ }
- {/* 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
setViewingMowingSession(log)}
className="p-2 text-indigo-600 hover:text-indigo-800 hover:bg-indigo-50 rounded"
+ title="View mowing session"
>
- );
- })}
-
+
+ );
+ })}
)}
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
+
+
setViewSession(s)}
+ >
+
+
+
+ ))}
+
+
+ )}
{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
-
-
setViewSession(s)}>View
+
+ setViewSession(s)}
+ >
+
+
))}