Files
turftracker/backend/src/routes/productSpreaderSettings.js
Jake Kasper e22cc10310 asdfas
2025-08-28 08:04:12 -05:00

291 lines
9.7 KiB
JavaScript

const express = require('express');
const pool = require('../config/database');
const { validateRequest, validateParams } = require('../utils/validation');
const { AppError } = require('../middleware/errorHandler');
const Joi = require('joi');
const router = express.Router();
// Validation schemas
const spreaderSettingSchema = Joi.object({
productId: Joi.number().integer().positive().optional(),
userProductId: Joi.number().integer().positive().optional(),
equipmentId: Joi.number().integer().positive().optional(), // Link to user_equipment
// Legacy fields for backward compatibility
spreaderBrand: Joi.string().max(100).optional(),
spreaderModel: Joi.string().max(100).allow(null, '').optional(),
settingValue: Joi.string().max(20).required(),
rateDescription: Joi.string().max(200).allow(null, '').optional(),
notes: Joi.string().allow(null, '').optional()
}).xor('productId', 'userProductId'); // Must have either productId or userProductId, but not both
const idParamSchema = Joi.object({
id: Joi.number().integer().positive().required()
});
const userProductIdParamSchema = Joi.object({
userProductId: Joi.number().integer().positive().required()
});
const productIdParamSchema = Joi.object({
productId: Joi.number().integer().positive().required()
});
// @route GET /api/product-spreader-settings/product/:productId
// @desc Get spreader settings for a specific product
// @access Private
router.get('/product/:productId', validateParams(productIdParamSchema), async (req, res, next) => {
try {
const productId = req.params.productId;
const result = await pool.query(
`SELECT pss.*, ue.custom_name as equipment_name, ue.manufacturer, ue.model as equipment_model
FROM product_spreader_settings pss
LEFT JOIN user_equipment ue ON pss.equipment_id = ue.id
WHERE pss.product_id = $1
ORDER BY ue.custom_name NULLS LAST, pss.spreader_brand, pss.spreader_model NULLS LAST, pss.setting_value`,
[productId]
);
res.json({
success: true,
data: {
settings: result.rows.map(row => ({
id: row.id,
equipmentId: row.equipment_id,
equipmentName: row.equipment_name,
equipmentManufacturer: row.manufacturer,
equipmentModel: row.equipment_model,
// Legacy fields
spreaderBrand: row.spreader_brand,
spreaderModel: row.spreader_model,
settingValue: row.setting_value,
rateDescription: row.rate_description,
notes: row.notes,
createdAt: row.created_at
}))
}
});
} catch (error) {
next(error);
}
});
// @route GET /api/product-spreader-settings/user-product/:userProductId
// @desc Get spreader settings for a specific user product
// @access Private
router.get('/user-product/:userProductId', validateParams(userProductIdParamSchema), async (req, res, next) => {
try {
const userProductId = req.params.userProductId;
// Verify the user product belongs to the requesting user
const productCheck = await pool.query(
'SELECT id FROM user_products WHERE id = $1 AND user_id = $2',
[userProductId, req.user.id]
);
if (productCheck.rows.length === 0) {
throw new AppError('User product not found', 404);
}
const result = await pool.query(
`SELECT pss.*, ue.custom_name as equipment_name, ue.manufacturer, ue.model as equipment_model
FROM product_spreader_settings pss
LEFT JOIN user_equipment ue ON pss.equipment_id = ue.id
WHERE pss.user_product_id = $1
ORDER BY ue.custom_name NULLS LAST, pss.spreader_brand, pss.spreader_model NULLS LAST, pss.setting_value`,
[userProductId]
);
res.json({
success: true,
data: {
settings: result.rows.map(row => ({
id: row.id,
equipmentId: row.equipment_id,
equipmentName: row.equipment_name,
equipmentManufacturer: row.manufacturer,
equipmentModel: row.equipment_model,
// Legacy fields
spreaderBrand: row.spreader_brand,
spreaderModel: row.spreader_model,
settingValue: row.setting_value,
rateDescription: row.rate_description,
notes: row.notes,
createdAt: row.created_at
}))
}
});
} catch (error) {
next(error);
}
});
// @route POST /api/product-spreader-settings
// @desc Add spreader setting to product
// @access Private
router.post('/', validateRequest(spreaderSettingSchema), async (req, res, next) => {
try {
const { productId, userProductId, equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes } = req.body;
// If it's a user product, verify ownership
if (userProductId) {
const productCheck = await pool.query(
'SELECT id FROM user_products WHERE id = $1 AND user_id = $2',
[userProductId, req.user.id]
);
if (productCheck.rows.length === 0) {
throw new AppError('User product not found', 404);
}
}
// If equipment ID is provided, verify it belongs to the user
if (equipmentId) {
const equipmentCheck = await pool.query(
'SELECT id FROM user_equipment WHERE id = $1 AND user_id = $2',
[equipmentId, req.user.id]
);
if (equipmentCheck.rows.length === 0) {
throw new AppError('Equipment not found', 404);
}
}
const result = await pool.query(
`INSERT INTO product_spreader_settings
(product_id, user_product_id, equipment_id, spreader_brand, spreader_model, setting_value, rate_description, notes)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING *`,
[productId, userProductId, equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes]
);
const setting = result.rows[0];
res.status(201).json({
success: true,
message: 'Spreader setting added successfully',
data: {
setting: {
id: setting.id,
equipmentId: setting.equipment_id,
spreaderBrand: setting.spreader_brand,
spreaderModel: setting.spreader_model,
settingValue: setting.setting_value,
rateDescription: setting.rate_description,
notes: setting.notes,
createdAt: setting.created_at
}
}
});
} catch (error) {
next(error);
}
});
// @route PUT /api/product-spreader-settings/:id
// @desc Update spreader setting
// @access Private
router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettingSchema), async (req, res, next) => {
try {
const settingId = req.params.id;
const { productId, userProductId, equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes } = req.body;
// Check if setting exists and user has permission to edit it
let checkQuery;
let checkParams;
if (userProductId) {
checkQuery = `
SELECT pss.* FROM product_spreader_settings pss
JOIN user_products up ON pss.user_product_id = up.id
WHERE pss.id = $1 AND up.user_id = $2
`;
checkParams = [settingId, req.user.id];
} else {
// For shared products, any authenticated user can edit (you might want to restrict this)
checkQuery = 'SELECT * FROM product_spreader_settings WHERE id = $1';
checkParams = [settingId];
}
const settingCheck = await pool.query(checkQuery, checkParams);
if (settingCheck.rows.length === 0) {
throw new AppError('Spreader setting not found', 404);
}
// If equipment ID is provided, verify it belongs to the user
if (equipmentId) {
const equipmentCheck = await pool.query(
'SELECT id FROM user_equipment WHERE id = $1 AND user_id = $2',
[equipmentId, req.user.id]
);
if (equipmentCheck.rows.length === 0) {
throw new AppError('Equipment not found', 404);
}
}
const result = await pool.query(
`UPDATE product_spreader_settings
SET equipment_id = $1, spreader_brand = $2, spreader_model = $3, setting_value = $4,
rate_description = $5, notes = $6
WHERE id = $7
RETURNING *`,
[equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes, settingId]
);
const setting = result.rows[0];
res.json({
success: true,
message: 'Spreader setting updated successfully',
data: {
setting: {
id: setting.id,
equipmentId: setting.equipment_id,
spreaderBrand: setting.spreader_brand,
spreaderModel: setting.spreader_model,
settingValue: setting.setting_value,
rateDescription: setting.rate_description,
notes: setting.notes,
updatedAt: setting.updated_at
}
}
});
} catch (error) {
next(error);
}
});
// @route DELETE /api/product-spreader-settings/:id
// @desc Delete spreader setting
// @access Private
router.delete('/:id', validateParams(idParamSchema), async (req, res, next) => {
try {
const settingId = req.params.id;
// Check if setting exists and user has permission to delete it
const settingCheck = await pool.query(
`SELECT pss.* FROM product_spreader_settings pss
LEFT JOIN user_products up ON pss.user_product_id = up.id
WHERE pss.id = $1 AND (pss.product_id IS NOT NULL OR up.user_id = $2)`,
[settingId, req.user.id]
);
if (settingCheck.rows.length === 0) {
throw new AppError('Spreader setting not found', 404);
}
await pool.query('DELETE FROM product_spreader_settings WHERE id = $1', [settingId]);
res.json({
success: true,
message: 'Spreader setting deleted successfully'
});
} catch (error) {
next(error);
}
});
module.exports = router;