diff --git a/backend/src/routes/mowing.js b/backend/src/routes/mowing.js
index c101ecf..6b5b78b 100644
--- a/backend/src/routes/mowing.js
+++ b/backend/src/routes/mowing.js
@@ -84,11 +84,16 @@ router.post('/sessions', validateRequest(mowingSessionSchema), async (req, res,
router.get('/sessions', async (req, res, next) => {
try {
const result = await pool.query(
- `SELECT ms.*, p.name as property_name, ue.custom_name as equipment_name
+ `SELECT ms.*, p.name as property_name, ue.custom_name as equipment_name,
+ STRING_AGG(ls.name, ', ') as section_names,
+ SUM(ls.area) as total_area
FROM mowing_sessions ms
JOIN properties p ON ms.property_id=p.id
LEFT JOIN user_equipment ue ON ms.equipment_id=ue.id
+ JOIN mowing_session_sections lss ON lss.session_id = ms.id
+ JOIN lawn_sections ls ON lss.lawn_section_id = ls.id
WHERE ms.user_id=$1
+ GROUP BY ms.id, p.name, ue.custom_name
ORDER BY ms.created_at DESC
LIMIT 200`,
[req.user.id]
@@ -214,11 +219,16 @@ router.put('/plans/:id/status', async (req, res, next) => {
router.get('/logs', async (req, res, next) => {
try {
const rs = await pool.query(
- `SELECT ms.*, p.name as property_name, ue.custom_name as equipment_name
+ `SELECT ms.*, p.name as property_name, ue.custom_name as equipment_name,
+ STRING_AGG(ls.name, ', ') as section_names,
+ SUM(ls.area) as total_area
FROM mowing_sessions ms
JOIN properties p ON ms.property_id=p.id
LEFT JOIN user_equipment ue ON ms.equipment_id=ue.id
+ JOIN mowing_session_sections lss ON lss.session_id = ms.id
+ JOIN lawn_sections ls ON lss.lawn_section_id = ls.id
WHERE ms.user_id=$1
+ GROUP BY ms.id, p.name, ue.custom_name
ORDER BY ms.created_at DESC
LIMIT 200`, [req.user.id]);
res.json({ success: true, data: { logs: rs.rows } });
diff --git a/frontend/src/pages/History/History.js b/frontend/src/pages/History/History.js
index b78c41c..38f1708 100644
--- a/frontend/src/pages/History/History.js
+++ b/frontend/src/pages/History/History.js
@@ -368,6 +368,19 @@ const History = () => {
return items;
})();
+ // Coverage calculation for mowing when shown in unified list
+ const calculateMowingCoverage = (log) => {
+ const meters = (log.total_distance_meters || log.gpsTrack?.totalDistance || log.gps_track?.totalDistance || 0) || 0;
+ const totalDistanceFeet = meters * 3.28084;
+ const plannedArea = Number(log.total_area || 0);
+ if (totalDistanceFeet === 0 || plannedArea === 0) return 0;
+ let equipmentWidth = 6; // default mower deck width in ft
+ const name = (log.equipment_name || '').toLowerCase();
+ if (name.includes('30"') || name.includes('30 in')) equipmentWidth = 2.5;
+ const theoreticalCoverageArea = totalDistanceFeet * equipmentWidth;
+ return Math.min(100, Math.round((theoreticalCoverageArea / plannedArea) * 100));
+ };
+
if (loading) {
return (
@@ -655,9 +668,12 @@ const History = () => {
-
- {application.propertyName} - {application.sectionNames}
-
+
+
+ {application.propertyName} - {application.sectionNames}
+
+ Application
+
{
// Mowing entry
const log = item.log;
- const durationMin = Math.round((log.duration_seconds || log.durationSeconds || log.gpsTrack?.duration || 0) / 60);
+ const durationMin = Math.round((log.duration_seconds || log.durationSeconds || log.gpsTrack?.duration || log.gps_track?.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);
+ const distFeet = Math.round(((log.total_distance_meters || log.gpsTrack?.totalDistance || log.gps_track?.totalDistance || 0) * 3.28084) || 0);
return (
-
-
-
{log.property_name || 'Property'}
-
{new Date(item.date).toLocaleString()} • {log.equipment_name || 'Mower'} • {avg} mph • {distFeet} ft • {durationMin} min
+
+
+
+
+
+ {(log.property_name || 'Property')} - {(log.section_names || 'Areas')}
+
+ Mowing
+
+
Completed
+
+
+
+
+
+ {new Date(item.date).toLocaleString()}
+
+
+
+ {log.property_name}
+
+
+
+ {log.equipment_name || 'Mower'}
+
+
+
+
+
+
Duration
+
{durationMin} min
+
+
+
GPS Points
+
{log.gpsTrack?.points?.length || log.gps_track?.points?.length || 0}
+
+
+
Distance
+
{distFeet} ft
+
+
+
Coverage
+
{calculateMowingCoverage(log)}%
+
+
+
+
+
-
);
diff --git a/frontend/src/pages/Mowing/Mowing.js b/frontend/src/pages/Mowing/Mowing.js
index 5f5e3d5..999e02d 100644
--- a/frontend/src/pages/Mowing/Mowing.js
+++ b/frontend/src/pages/Mowing/Mowing.js
@@ -12,7 +12,6 @@ const Mowing = () => {
const [loading, setLoading] = useState(true);
const [showPlanModal, setShowPlanModal] = useState(false);
const [execPlan, setExecPlan] = useState(null);
- const [sessions, setSessions] = useState([]);
const [viewSession, setViewSession] = useState(null);
const fetchPlans = async () => {
@@ -20,11 +19,6 @@ const Mowing = () => {
setLoading(true);
const r = await mowingAPI.getPlans();
setPlans(r.data.data.plans || []);
- // also load recent sessions
- try {
- const s = await mowingAPI.getLogs();
- setSessions(s.data?.data?.logs || []);
- } catch {}
} catch (e) {
toast.error('Failed to load mowing plans');
} finally {
@@ -75,29 +69,6 @@ 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'}
@@ -127,40 +98,7 @@ const Mowing = () => {
setExecPlan(null)} onComplete={fetchPlans} />
)}
- {/* Recent Sessions */}
-
-
Recent Sessions
-
-
-
Property
-
Mower
-
Cut Height
-
Avg Speed
-
Distance
-
Actions
-
- {sessions.length === 0 ? (
-
No sessions yet.
- ) : sessions.map((s) => (
-
-
{s.property_name}
-
{s.equipment_name || '—'}
-
{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
-
-
-
-
- ))}
-
-
+ {/* Recent Sessions section removed as requested */}
{viewSession && (
setViewSession(null)} />