mowing modal
This commit is contained in:
@@ -2,7 +2,7 @@ const express = require('express');
|
||||
const pool = require('../config/database');
|
||||
const { AppError } = require('../middleware/errorHandler');
|
||||
const { validateRequest } = require('../utils/validation');
|
||||
const { mowingSessionSchema } = require('../utils/validation');
|
||||
const { mowingSessionSchema, mowingPlanSchema } = require('../utils/validation');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -127,3 +127,100 @@ router.get('/sessions/:id', async (req, res, next) => {
|
||||
|
||||
module.exports = router;
|
||||
|
||||
// ----- Plans -----
|
||||
router.post('/plans', validateRequest(mowingPlanSchema), async (req, res, next) => {
|
||||
try {
|
||||
const { propertyId, lawnSectionIds, equipmentId, plannedDate, cutHeightInches, direction, notes } = req.body;
|
||||
// Ownership checks
|
||||
const prop = await pool.query('SELECT id FROM properties WHERE id=$1 AND user_id=$2', [propertyId, req.user.id]);
|
||||
if (prop.rows.length === 0) throw new AppError('Property not found', 404);
|
||||
const equip = await pool.query('SELECT id FROM user_equipment WHERE id=$1 AND user_id=$2', [equipmentId, req.user.id]);
|
||||
if (equip.rows.length === 0) throw new AppError('Equipment not found', 404);
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
const ins = await client.query(
|
||||
`INSERT INTO mowing_plans (user_id, property_id, equipment_id, planned_date, cut_height_inches, direction, notes)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING *`,
|
||||
[req.user.id, propertyId, equipmentId, plannedDate, cutHeightInches, direction, notes || null]
|
||||
);
|
||||
const plan = ins.rows[0];
|
||||
for (const sid of lawnSectionIds) {
|
||||
await client.query(`INSERT INTO mowing_plan_sections (plan_id, lawn_section_id) VALUES ($1,$2)`, [plan.id, sid]);
|
||||
}
|
||||
await client.query('COMMIT');
|
||||
res.status(201).json({ success: true, data: { plan: { id: plan.id } } });
|
||||
} catch (e) {
|
||||
await client.query('ROLLBACK');
|
||||
throw e;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
} catch (error) { next(error); }
|
||||
});
|
||||
|
||||
router.get('/plans', async (req, res, next) => {
|
||||
try {
|
||||
const rows = await pool.query(
|
||||
`SELECT mp.*, 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_plans mp
|
||||
JOIN properties p ON mp.property_id=p.id
|
||||
LEFT JOIN user_equipment ue ON mp.equipment_id=ue.id
|
||||
JOIN mowing_plan_sections mps ON mp.id=mps.plan_id
|
||||
JOIN lawn_sections ls ON mps.lawn_section_id=ls.id
|
||||
WHERE mp.user_id=$1
|
||||
GROUP BY mp.id, p.name, ue.custom_name
|
||||
ORDER BY mp.planned_date DESC, mp.created_at DESC
|
||||
LIMIT 200`,
|
||||
[req.user.id]
|
||||
);
|
||||
res.json({ success: true, data: { plans: rows.rows } });
|
||||
} catch (error) { next(error); }
|
||||
});
|
||||
|
||||
router.get('/plans/:id', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const planRes = await pool.query(
|
||||
`SELECT mp.*, p.name as property_name, ue.custom_name as equipment_name
|
||||
FROM mowing_plans mp
|
||||
JOIN properties p ON mp.property_id=p.id
|
||||
LEFT JOIN user_equipment ue ON mp.equipment_id=ue.id
|
||||
WHERE mp.id=$1 AND mp.user_id=$2`, [id, req.user.id]);
|
||||
if (planRes.rows.length === 0) throw new AppError('Plan not found', 404);
|
||||
const sectionsRes = await pool.query(
|
||||
`SELECT ls.id, ls.name, ls.area, ls.polygon_data
|
||||
FROM mowing_plan_sections mps
|
||||
JOIN lawn_sections ls ON mps.lawn_section_id=ls.id
|
||||
WHERE mps.plan_id=$1`, [id]);
|
||||
res.json({ success: true, data: { plan: planRes.rows[0], sections: sectionsRes.rows } });
|
||||
} catch (error) { next(error); }
|
||||
});
|
||||
|
||||
router.put('/plans/:id/status', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = req.params; const { status } = req.body;
|
||||
const ok = ['planned','in_progress','completed','archived'];
|
||||
if (!ok.includes(status)) throw new AppError('Invalid status', 400);
|
||||
const upd = await pool.query(`UPDATE mowing_plans SET status=$1, updated_at=CURRENT_TIMESTAMP WHERE id=$2 AND user_id=$3`, [status, id, req.user.id]);
|
||||
if (upd.rowCount === 0) throw new AppError('Plan not found', 404);
|
||||
res.json({ success: true });
|
||||
} catch (error) { next(error); }
|
||||
});
|
||||
|
||||
// Alias /logs to sessions to be consistent with Applications
|
||||
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
|
||||
FROM mowing_sessions ms
|
||||
JOIN properties p ON ms.property_id=p.id
|
||||
LEFT JOIN user_equipment ue ON ms.equipment_id=ue.id
|
||||
WHERE ms.user_id=$1
|
||||
ORDER BY ms.created_at DESC
|
||||
LIMIT 200`, [req.user.id]);
|
||||
res.json({ success: true, data: { logs: rs.rows } });
|
||||
} catch (error) { next(error); }
|
||||
});
|
||||
|
||||
@@ -195,6 +195,16 @@ const mowingSessionSchema = Joi.object({
|
||||
notes: Joi.string().allow('').optional()
|
||||
});
|
||||
|
||||
const mowingPlanSchema = Joi.object({
|
||||
propertyId: Joi.number().integer().positive().required(),
|
||||
lawnSectionIds: Joi.array().items(Joi.number().integer().positive()).min(1).required(),
|
||||
equipmentId: Joi.number().integer().positive().required(),
|
||||
plannedDate: Joi.date().required(),
|
||||
cutHeightInches: Joi.number().positive().precision(2).required(),
|
||||
direction: Joi.string().valid('N_S','E_W','NE_SW','NW_SE','CIRCULAR').required(),
|
||||
notes: Joi.string().allow('').optional()
|
||||
});
|
||||
|
||||
// Validation middleware
|
||||
const validateRequest = (schema) => {
|
||||
return (req, res, next) => {
|
||||
@@ -235,6 +245,7 @@ module.exports = {
|
||||
applicationPlanSchema,
|
||||
applicationLogSchema,
|
||||
mowingSessionSchema,
|
||||
mowingPlanSchema,
|
||||
idParamSchema,
|
||||
|
||||
// Middleware
|
||||
|
||||
Reference in New Issue
Block a user