diff --git a/backend/src/routes/watering.js b/backend/src/routes/watering.js index ec198af..02f3296 100644 --- a/backend/src/routes/watering.js +++ b/backend/src/routes/watering.js @@ -184,13 +184,27 @@ router.put('/points/:id', async (req, res, next) => { router.delete('/points/:id', async (req,res,next)=>{ try { const pointId = req.params.id; - const own = await pool.query( - `SELECT wpp.id FROM watering_plan_points wpp + // Verify and fetch plan id for resequencing + const chk = await pool.query( + `SELECT wpp.plan_id FROM watering_plan_points wpp JOIN watering_plans wp ON wpp.plan_id = wp.id WHERE wpp.id=$1 AND wp.user_id=$2`, [pointId, req.user.id] ); - if (own.rows.length === 0) throw new AppError('Point not found', 404); + if (chk.rows.length === 0) throw new AppError('Point not found', 404); + const planId = chk.rows[0].plan_id; await pool.query('DELETE FROM watering_plan_points WHERE id=$1', [pointId]); + // Resequence remaining points for the plan + await pool.query( + `WITH ordered AS ( + SELECT id, ROW_NUMBER() OVER (ORDER BY sequence, id) AS rn + FROM watering_plan_points WHERE plan_id=$1 + ) + UPDATE watering_plan_points w + SET sequence = o.rn + FROM ordered o + WHERE w.id = o.id`, + [planId] + ); res.json({ success:true }); } catch (e) { next(e); } }); diff --git a/frontend/src/pages/Watering/Watering.js b/frontend/src/pages/Watering/Watering.js index 638bdb1..3493709 100644 --- a/frontend/src/pages/Watering/Watering.js +++ b/frontend/src/pages/Watering/Watering.js @@ -1,5 +1,5 @@ import React, { useEffect, useMemo, useState } from 'react'; -import { MapContainer, TileLayer, Polygon, Marker, Circle, Rectangle, Popup, useMapEvents, useMap } from 'react-leaflet'; +import { MapContainer, TileLayer, Polygon, Marker, Circle, Rectangle, Popup, useMapEvents, useMap, Polyline } from 'react-leaflet'; import { Icon } from 'leaflet'; import 'leaflet/dist/leaflet.css'; import { propertiesAPI, wateringAPI, equipmentAPI } from '../../services/api'; @@ -271,6 +271,15 @@ const Watering = () => { return (2*R*Math.atan2(Math.sqrt(A),Math.sqrt(1-A)))*3.28084; }; + const bearingDegrees = (a,b) => { + const toRad = d=> d*Math.PI/180, toDeg = r=> (r*180/Math.PI+360)%360; + const lat1 = toRad(a.lat), lat2 = toRad(b.lat); + const dLng = toRad(b.lng - a.lng); + const y = Math.sin(dLng) * Math.cos(lat2); + const x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLng); + return Math.round(toDeg(Math.atan2(y,x))); + }; + // Build sector polygon (approx) for Leaflet const sectorPolygon = (center, radiusFeet, startDeg, endDeg, steps=60) => { const [clat, clng] = [Number(center.lat), Number(center.lng)]; @@ -560,7 +569,12 @@ const Watering = () => { }}>Save @@ -572,9 +586,15 @@ const Watering = () => {
-
{guiding && currentPos && points[guideIndex] ? ( - <>Go to point #{points[guideIndex].sequence}: {distanceFeet(currentPos, {lat: Number(points[guideIndex].lat), lng: Number(points[guideIndex].lng)}).toFixed(0)} ft away - ) : 'Map'}
+
{guiding && currentPos && points[guideIndex] ? (()=>{ + const tgt = {lat:Number(points[guideIndex].lat), lng:Number(points[guideIndex].lng)}; + const dist = distanceFeet(currentPos, tgt); + const brg = bearingDegrees(currentPos, tgt); + if (dist <= 15) { + return Arrived at #{points[guideIndex].sequence} • {dist.toFixed(0)} ft; + } + return <>→ {brg}° • {dist.toFixed(0)} ft • Point #{points[guideIndex].sequence}; + })() : 'Map'}