watering attempt 1
This commit is contained in:
@@ -19,6 +19,7 @@ const weatherRoutes = require('./routes/weather');
|
||||
const weatherPublicRoutes = require('./routes/weatherPublic');
|
||||
const adminRoutes = require('./routes/admin');
|
||||
const mowingRoutes = require('./routes/mowing');
|
||||
const wateringRoutes = require('./routes/watering');
|
||||
|
||||
const { errorHandler } = require('./middleware/errorHandler');
|
||||
const { authenticateToken } = require('./middleware/auth');
|
||||
@@ -106,6 +107,7 @@ app.use('/api/nozzles', authenticateToken, nozzleRoutes);
|
||||
app.use('/api/products', authenticateToken, productRoutes);
|
||||
app.use('/api/applications', authenticateToken, applicationRoutes);
|
||||
app.use('/api/mowing', authenticateToken, mowingRoutes);
|
||||
app.use('/api/watering', authenticateToken, wateringRoutes);
|
||||
app.use('/api/spreader-settings', authenticateToken, spreaderSettingsRoutes);
|
||||
app.use('/api/product-spreader-settings', authenticateToken, productSpreaderSettingsRoutes);
|
||||
app.use('/api/weather', authenticateToken, weatherRoutes);
|
||||
|
||||
105
backend/src/routes/watering.js
Normal file
105
backend/src/routes/watering.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const express = require('express');
|
||||
const pool = require('../config/database');
|
||||
const { AppError } = require('../middleware/errorHandler');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Compute coverage area in sqft (server-side helper)
|
||||
const computeCoverageSqft = (point) => {
|
||||
const {
|
||||
sprinkler_head_type,
|
||||
sprinkler_throw_feet,
|
||||
sprinkler_degrees,
|
||||
sprinkler_length_feet,
|
||||
sprinkler_width_feet
|
||||
} = point;
|
||||
if (sprinkler_head_type === 'rotor_impact' || sprinkler_head_type === 'spray_fixed') {
|
||||
const r = Number(sprinkler_throw_feet || 0);
|
||||
const deg = Math.min(360, Math.max(0, Number(sprinkler_degrees || 360)));
|
||||
const area = Math.PI * r * r * (deg / 360);
|
||||
return Math.round(area * 100) / 100;
|
||||
}
|
||||
if (sprinkler_head_type === 'oscillating_fan') {
|
||||
const L = Number(sprinkler_length_feet || 0);
|
||||
const W = Number(sprinkler_width_feet || 0);
|
||||
const area = L * W;
|
||||
return Math.round(area * 100) / 100;
|
||||
}
|
||||
if (sprinkler_head_type === 'drip') {
|
||||
return 0; // coverage represented differently; skip for now
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
// GET /api/watering/plans?property_id=...
|
||||
router.get('/plans', async (req, res, next) => {
|
||||
try {
|
||||
const { property_id } = req.query;
|
||||
const rows = await pool.query(
|
||||
`SELECT * FROM watering_plans WHERE user_id=$1 ${property_id? 'AND property_id=$2':''} ORDER BY created_at DESC`,
|
||||
property_id? [req.user.id, property_id] : [req.user.id]
|
||||
);
|
||||
res.json({ success:true, data:{ plans: rows.rows }});
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
// POST /api/watering/plans
|
||||
router.post('/plans', async (req, res, next) => {
|
||||
try {
|
||||
const { propertyId, name, notes } = req.body;
|
||||
if (!propertyId || !name) throw new AppError('propertyId and name required', 400);
|
||||
// Verify property ownership
|
||||
const pr = await pool.query('SELECT id FROM properties WHERE id=$1 AND user_id=$2', [propertyId, req.user.id]);
|
||||
if (pr.rows.length === 0) throw new AppError('Property not found', 404);
|
||||
|
||||
const ins = await pool.query(
|
||||
`INSERT INTO watering_plans(user_id, property_id, name, notes) VALUES ($1,$2,$3,$4) RETURNING *`,
|
||||
[req.user.id, propertyId, name, notes||null]
|
||||
);
|
||||
res.status(201).json({ success:true, data:{ plan: ins.rows[0] }});
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
// GET /api/watering/plans/:id/points
|
||||
router.get('/plans/:id/points', async (req, res, next) => {
|
||||
try {
|
||||
const planId = req.params.id;
|
||||
const ch = await pool.query('SELECT id FROM watering_plans WHERE id=$1 AND user_id=$2',[planId, req.user.id]);
|
||||
if (ch.rows.length===0) throw new AppError('Plan not found',404);
|
||||
const rs = await pool.query('SELECT * FROM watering_plan_points WHERE plan_id=$1 ORDER BY sequence', [planId]);
|
||||
res.json({ success:true, data:{ points: rs.rows }});
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
// POST /api/watering/plans/:id/points
|
||||
router.post('/plans/:id/points', async (req,res,next)=>{
|
||||
try {
|
||||
const planId = req.params.id;
|
||||
const ch = await pool.query('SELECT id, property_id FROM watering_plans WHERE id=$1 AND user_id=$2',[planId, req.user.id]);
|
||||
if (ch.rows.length===0) throw new AppError('Plan not found',404);
|
||||
const seqrs = await pool.query('SELECT COALESCE(MAX(sequence),0)+1 as next FROM watering_plan_points WHERE plan_id=$1',[planId]);
|
||||
const sequence = seqrs.rows[0].next;
|
||||
const payload = req.body || {};
|
||||
const point = {
|
||||
sprinkler_head_type: payload.sprinklerHeadType,
|
||||
sprinkler_throw_feet: payload.throwFeet,
|
||||
sprinkler_degrees: payload.degrees,
|
||||
sprinkler_length_feet: payload.lengthFeet,
|
||||
sprinkler_width_feet: payload.widthFeet
|
||||
};
|
||||
const coverage = computeCoverageSqft(point);
|
||||
const ins = await pool.query(
|
||||
`INSERT INTO watering_plan_points
|
||||
(plan_id, sequence, lat, lng, duration_minutes, sprinkler_mount, sprinkler_head_type,
|
||||
sprinkler_gpm, sprinkler_throw_feet, sprinkler_degrees, sprinkler_length_feet, sprinkler_width_feet, coverage_sqft)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) RETURNING *`,
|
||||
[planId, sequence, payload.lat, payload.lng, payload.durationMinutes||0, payload.mountType||null,
|
||||
payload.sprinklerHeadType||null, payload.gpm||null, payload.throwFeet||null, payload.degrees||null,
|
||||
payload.lengthFeet||null, payload.widthFeet||null, coverage]
|
||||
);
|
||||
res.status(201).json({ success:true, data:{ point: ins.rows[0] }});
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user