boundary entries updates

This commit is contained in:
Jake Kasper
2025-09-04 07:29:41 -05:00
parent a5d2cf3ca0
commit 9a1640af37
8 changed files with 607 additions and 18 deletions

View File

@@ -1124,3 +1124,72 @@ router.put('/settings', async (req, res, next) => {
});
module.exports = router;
// ----- Properties management -----
// List properties (with user info)
router.get('/properties', async (req, res, next) => {
try {
const { search, user_id } = req.query;
let where = [];
let params = [];
let n = 0;
if (user_id) { n++; where.push(`p.user_id=$${n}`); params.push(user_id); }
if (search) { n++; where.push(`(p.name ILIKE $${n} OR p.address ILIKE $${n} OR u.email ILIKE $${n})`); params.push(`%${search}%`); }
const whereClause = where.length ? `WHERE ${where.join(' AND ')}` : '';
const rs = await pool.query(`
SELECT p.*, u.email, u.first_name, u.last_name,
COUNT(ls.id) as section_count, COALESCE(SUM(ls.area),0) as calculated_area
FROM properties p
JOIN users u ON p.user_id=u.id
LEFT JOIN lawn_sections ls ON ls.property_id=p.id
${whereClause}
GROUP BY p.id, u.email, u.first_name, u.last_name
ORDER BY p.created_at DESC
`, params);
res.json({ success: true, data: { properties: rs.rows.map(r=>({
id: r.id, userId: r.user_id, userEmail: r.email, userName: `${r.first_name} ${r.last_name}`,
name: r.name, address: r.address, latitude: parseFloat(r.latitude), longitude: parseFloat(r.longitude),
totalArea: parseFloat(r.total_area), calculatedArea: parseFloat(r.calculated_area), sectionCount: parseInt(r.section_count),
createdAt: r.created_at, updatedAt: r.updated_at
})) }});
} catch (e) { next(e); }
});
// Get property with sections (admin override)
router.get('/properties/:id', validateParams(idParamSchema), async (req, res, next) => {
try {
const { id } = req.params;
const pr = await pool.query(`SELECT p.*, u.email FROM properties p JOIN users u ON p.user_id=u.id WHERE p.id=$1`, [id]);
if (pr.rows.length===0) throw new AppError('Property not found', 404);
const srs = await pool.query(`SELECT * FROM lawn_sections WHERE property_id=$1 ORDER BY name`, [id]);
const p = pr.rows[0];
res.json({ success:true, data:{ property: {
id: p.id, userId: p.user_id, userEmail: p.email, name: p.name, address: p.address,
latitude: parseFloat(p.latitude), longitude: parseFloat(p.longitude), totalArea: parseFloat(p.total_area),
createdAt: p.created_at, updatedAt: p.updated_at,
sections: srs.rows.map(s=>({ id:s.id, name:s.name, area: parseFloat(s.area), polygonData: s.polygon_data, grassType: s.grass_type, grassTypes: s.grass_types, captureMethod: s.capture_method, captureMeta: s.capture_meta, createdAt:s.created_at, updatedAt:s.updated_at }))
}}});
} catch (e) { next(e); }
});
// Update a lawn section (admin override)
router.put('/properties/:propertyId/sections/:sectionId', validateParams(idParamSchema), async (req, res, next) => {
try {
const { propertyId, sectionId } = req.params;
const { name, area, polygonData, grassType, grassTypes, captureMethod, captureMeta } = req.body;
const own = await pool.query('SELECT id FROM properties WHERE id=$1', [propertyId]);
if (own.rows.length===0) throw new AppError('Property not found', 404);
// optional sanitize lite
let poly = polygonData;
try {
const coords = polygonData?.coordinates?.[0] || [];
let filtered = coords.filter(Boolean);
if (filtered.length>=3) poly = { ...polygonData, coordinates: [filtered] };
} catch {}
const rs = await pool.query(`UPDATE lawn_sections SET name=$1, area=$2, polygon_data=$3, grass_type=$4, grass_types=$5, capture_method=$6, capture_meta=$7, updated_at=CURRENT_TIMESTAMP WHERE id=$8 AND property_id=$9 RETURNING *`, [
name, area, JSON.stringify(poly), grassType || (Array.isArray(grassTypes)? grassTypes.join(', '): null), grassTypes? JSON.stringify(grassTypes): null, captureMethod||null, captureMeta? JSON.stringify(captureMeta): null, sectionId, propertyId
]);
if (rs.rows.length===0) throw new AppError('Section not found', 404);
const s=rs.rows[0];
res.json({ success:true, data:{ section:{ id:s.id, name:s.name, area: parseFloat(s.area), polygonData: s.polygon_data, grassType: s.grass_type, grassTypes: s.grass_types, captureMethod: s.capture_method, createdAt:s.created_at, updatedAt:s.updated_at }}});
} catch (e) { next(e); }
});