equipment stuff
This commit is contained in:
503
backend/src/routes/nozzles.js
Normal file
503
backend/src/routes/nozzles.js
Normal file
@@ -0,0 +1,503 @@
|
||||
const express = require('express');
|
||||
const pool = require('../config/database');
|
||||
const { validateRequest, validateParams } = require('../utils/validation');
|
||||
const { idParamSchema } = require('../utils/validation');
|
||||
const { AppError } = require('../middleware/errorHandler');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// @route GET /api/nozzles/types
|
||||
// @desc Get all nozzle types
|
||||
// @access Private
|
||||
router.get('/types', async (req, res, next) => {
|
||||
try {
|
||||
const { manufacturer, droplet_size, spray_pattern } = req.query;
|
||||
|
||||
let query = 'SELECT * FROM nozzle_types WHERE 1=1';
|
||||
const queryParams = [];
|
||||
|
||||
if (manufacturer) {
|
||||
queryParams.push(manufacturer);
|
||||
query += ` AND manufacturer ILIKE $${queryParams.length}`;
|
||||
}
|
||||
|
||||
if (droplet_size) {
|
||||
queryParams.push(droplet_size);
|
||||
query += ` AND droplet_size = $${queryParams.length}`;
|
||||
}
|
||||
|
||||
if (spray_pattern) {
|
||||
queryParams.push(spray_pattern);
|
||||
query += ` AND spray_pattern = $${queryParams.length}`;
|
||||
}
|
||||
|
||||
query += ' ORDER BY manufacturer, name';
|
||||
|
||||
const result = await pool.query(query, queryParams);
|
||||
|
||||
// Group by manufacturer for easier frontend handling
|
||||
const nozzlesByManufacturer = result.rows.reduce((acc, nozzle) => {
|
||||
const manufacturer = nozzle.manufacturer || 'Unknown';
|
||||
if (!acc[manufacturer]) {
|
||||
acc[manufacturer] = [];
|
||||
}
|
||||
acc[manufacturer].push({
|
||||
id: nozzle.id,
|
||||
name: nozzle.name,
|
||||
manufacturer: nozzle.manufacturer,
|
||||
model: nozzle.model,
|
||||
orificeSize: nozzle.orifice_size,
|
||||
sprayAngle: nozzle.spray_angle,
|
||||
flowRateGpm: parseFloat(nozzle.flow_rate_gpm),
|
||||
dropletSize: nozzle.droplet_size,
|
||||
sprayPattern: nozzle.spray_pattern,
|
||||
pressureRangePsi: nozzle.pressure_range_psi,
|
||||
createdAt: nozzle.created_at
|
||||
});
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
nozzleTypes: result.rows.map(nt => ({
|
||||
id: nt.id,
|
||||
name: nt.name,
|
||||
manufacturer: nt.manufacturer,
|
||||
model: nt.model,
|
||||
orificeSize: nt.orifice_size,
|
||||
sprayAngle: nt.spray_angle,
|
||||
flowRateGpm: parseFloat(nt.flow_rate_gpm),
|
||||
dropletSize: nt.droplet_size,
|
||||
sprayPattern: nt.spray_pattern,
|
||||
pressureRangePsi: nt.pressure_range_psi,
|
||||
createdAt: nt.created_at
|
||||
})),
|
||||
nozzlesByManufacturer
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET /api/nozzles
|
||||
// @desc Get all user's nozzles
|
||||
// @access Private
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
`SELECT un.*, nt.name as type_name, nt.manufacturer as type_manufacturer,
|
||||
nt.model as type_model, nt.orifice_size, nt.spray_angle, nt.flow_rate_gpm,
|
||||
nt.droplet_size, nt.spray_pattern, nt.pressure_range_psi
|
||||
FROM user_nozzles un
|
||||
LEFT JOIN nozzle_types nt ON un.nozzle_type_id = nt.id
|
||||
WHERE un.user_id = $1
|
||||
ORDER BY nt.manufacturer, nt.name, un.custom_name`,
|
||||
[req.user.id]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
nozzles: result.rows.map(item => ({
|
||||
id: item.id,
|
||||
nozzleTypeId: item.nozzle_type_id,
|
||||
typeName: item.type_name,
|
||||
typeManufacturer: item.type_manufacturer,
|
||||
typeModel: item.type_model,
|
||||
orificeSize: item.orifice_size,
|
||||
sprayAngle: item.spray_angle,
|
||||
flowRateGpm: parseFloat(item.flow_rate_gpm),
|
||||
dropletSize: item.droplet_size,
|
||||
sprayPattern: item.spray_pattern,
|
||||
pressureRangePsi: item.pressure_range_psi,
|
||||
customName: item.custom_name,
|
||||
quantity: item.quantity,
|
||||
condition: item.condition,
|
||||
purchaseDate: item.purchase_date,
|
||||
notes: item.notes,
|
||||
createdAt: item.created_at,
|
||||
updatedAt: item.updated_at
|
||||
}))
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET /api/nozzles/:id
|
||||
// @desc Get single user nozzle
|
||||
// @access Private
|
||||
router.get('/:id', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const nozzleId = req.params.id;
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT un.*, nt.name as type_name, nt.manufacturer as type_manufacturer,
|
||||
nt.model as type_model, nt.orifice_size, nt.spray_angle, nt.flow_rate_gpm,
|
||||
nt.droplet_size, nt.spray_pattern, nt.pressure_range_psi
|
||||
FROM user_nozzles un
|
||||
LEFT JOIN nozzle_types nt ON un.nozzle_type_id = nt.id
|
||||
WHERE un.id = $1 AND un.user_id = $2`,
|
||||
[nozzleId, req.user.id]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
throw new AppError('Nozzle not found', 404);
|
||||
}
|
||||
|
||||
const item = result.rows[0];
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
nozzle: {
|
||||
id: item.id,
|
||||
nozzleTypeId: item.nozzle_type_id,
|
||||
typeName: item.type_name,
|
||||
typeManufacturer: item.type_manufacturer,
|
||||
typeModel: item.type_model,
|
||||
orificeSize: item.orifice_size,
|
||||
sprayAngle: item.spray_angle,
|
||||
flowRateGpm: parseFloat(item.flow_rate_gpm),
|
||||
dropletSize: item.droplet_size,
|
||||
sprayPattern: item.spray_pattern,
|
||||
pressureRangePsi: item.pressure_range_psi,
|
||||
customName: item.custom_name,
|
||||
quantity: item.quantity,
|
||||
condition: item.condition,
|
||||
purchaseDate: item.purchase_date,
|
||||
notes: item.notes,
|
||||
createdAt: item.created_at,
|
||||
updatedAt: item.updated_at
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route POST /api/nozzles
|
||||
// @desc Add new nozzle to user's inventory
|
||||
// @access Private
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
const {
|
||||
nozzleTypeId,
|
||||
customName,
|
||||
quantity,
|
||||
condition,
|
||||
purchaseDate,
|
||||
notes
|
||||
} = req.body;
|
||||
|
||||
// Validate nozzle type exists if provided
|
||||
if (nozzleTypeId) {
|
||||
const typeCheck = await pool.query(
|
||||
'SELECT id, name, manufacturer FROM nozzle_types WHERE id = $1',
|
||||
[nozzleTypeId]
|
||||
);
|
||||
|
||||
if (typeCheck.rows.length === 0) {
|
||||
throw new AppError('Nozzle type not found', 404);
|
||||
}
|
||||
}
|
||||
|
||||
// Either nozzleTypeId or customName is required
|
||||
if (!nozzleTypeId && !customName) {
|
||||
throw new AppError('Either nozzle type or custom name is required', 400);
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`INSERT INTO user_nozzles
|
||||
(user_id, nozzle_type_id, custom_name, quantity, condition, purchase_date, notes)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING *`,
|
||||
[req.user.id, nozzleTypeId, customName, quantity || 1, condition || 'good', purchaseDate, notes]
|
||||
);
|
||||
|
||||
const nozzle = result.rows[0];
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Nozzle added successfully',
|
||||
data: {
|
||||
nozzle: {
|
||||
id: nozzle.id,
|
||||
nozzleTypeId: nozzle.nozzle_type_id,
|
||||
customName: nozzle.custom_name,
|
||||
quantity: nozzle.quantity,
|
||||
condition: nozzle.condition,
|
||||
purchaseDate: nozzle.purchase_date,
|
||||
notes: nozzle.notes,
|
||||
createdAt: nozzle.created_at,
|
||||
updatedAt: nozzle.updated_at
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route PUT /api/nozzles/:id
|
||||
// @desc Update user nozzle
|
||||
// @access Private
|
||||
router.put('/:id', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const nozzleId = req.params.id;
|
||||
const {
|
||||
nozzleTypeId,
|
||||
customName,
|
||||
quantity,
|
||||
condition,
|
||||
purchaseDate,
|
||||
notes
|
||||
} = req.body;
|
||||
|
||||
// Check if nozzle exists and belongs to user
|
||||
const checkResult = await pool.query(
|
||||
'SELECT id FROM user_nozzles WHERE id = $1 AND user_id = $2',
|
||||
[nozzleId, req.user.id]
|
||||
);
|
||||
|
||||
if (checkResult.rows.length === 0) {
|
||||
throw new AppError('Nozzle not found', 404);
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`UPDATE user_nozzles
|
||||
SET nozzle_type_id = $1, custom_name = $2, quantity = $3, condition = $4,
|
||||
purchase_date = $5, notes = $6, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $7
|
||||
RETURNING *`,
|
||||
[nozzleTypeId, customName, quantity, condition, purchaseDate, notes, nozzleId]
|
||||
);
|
||||
|
||||
const nozzle = result.rows[0];
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Nozzle updated successfully',
|
||||
data: {
|
||||
nozzle: {
|
||||
id: nozzle.id,
|
||||
nozzleTypeId: nozzle.nozzle_type_id,
|
||||
customName: nozzle.custom_name,
|
||||
quantity: nozzle.quantity,
|
||||
condition: nozzle.condition,
|
||||
purchaseDate: nozzle.purchase_date,
|
||||
notes: nozzle.notes,
|
||||
createdAt: nozzle.created_at,
|
||||
updatedAt: nozzle.updated_at
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route DELETE /api/nozzles/:id
|
||||
// @desc Delete user nozzle
|
||||
// @access Private
|
||||
router.delete('/:id', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const nozzleId = req.params.id;
|
||||
|
||||
// Check if nozzle exists and belongs to user
|
||||
const checkResult = await pool.query(
|
||||
'SELECT id FROM user_nozzles WHERE id = $1 AND user_id = $2',
|
||||
[nozzleId, req.user.id]
|
||||
);
|
||||
|
||||
if (checkResult.rows.length === 0) {
|
||||
throw new AppError('Nozzle not found', 404);
|
||||
}
|
||||
|
||||
// Check if nozzle is assigned to any equipment
|
||||
const assignmentCheck = await pool.query(
|
||||
'SELECT COUNT(*) as count FROM equipment_nozzle_assignments WHERE user_nozzle_id = $1',
|
||||
[nozzleId]
|
||||
);
|
||||
|
||||
if (parseInt(assignmentCheck.rows[0].count) > 0) {
|
||||
throw new AppError('Cannot delete nozzle that is assigned to equipment', 400);
|
||||
}
|
||||
|
||||
await pool.query('DELETE FROM user_nozzles WHERE id = $1', [nozzleId]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Nozzle deleted successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET /api/nozzles/equipment/:equipmentId/assignments
|
||||
// @desc Get nozzle assignments for specific equipment
|
||||
// @access Private
|
||||
router.get('/equipment/:equipmentId/assignments', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const equipmentId = req.params.equipmentId;
|
||||
|
||||
// Verify equipment belongs to user
|
||||
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(
|
||||
`SELECT ena.*, un.custom_name as nozzle_custom_name, un.quantity as nozzle_total_quantity,
|
||||
nt.name as nozzle_type_name, nt.manufacturer, nt.model, nt.orifice_size,
|
||||
nt.spray_angle, nt.flow_rate_gpm, nt.droplet_size, nt.spray_pattern
|
||||
FROM equipment_nozzle_assignments ena
|
||||
JOIN user_nozzles un ON ena.user_nozzle_id = un.id
|
||||
LEFT JOIN nozzle_types nt ON un.nozzle_type_id = nt.id
|
||||
WHERE ena.user_equipment_id = $1
|
||||
ORDER BY ena.position, nt.manufacturer, nt.name`,
|
||||
[equipmentId]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
assignments: result.rows.map(item => ({
|
||||
id: item.id,
|
||||
userEquipmentId: item.user_equipment_id,
|
||||
userNozzleId: item.user_nozzle_id,
|
||||
position: item.position,
|
||||
quantityAssigned: item.quantity_assigned,
|
||||
assignedDate: item.assigned_date,
|
||||
nozzleCustomName: item.nozzle_custom_name,
|
||||
nozzleTotalQuantity: item.nozzle_total_quantity,
|
||||
nozzleTypeName: item.nozzle_type_name,
|
||||
manufacturer: item.manufacturer,
|
||||
model: item.model,
|
||||
orificeSize: item.orifice_size,
|
||||
sprayAngle: item.spray_angle,
|
||||
flowRateGpm: parseFloat(item.flow_rate_gpm),
|
||||
dropletSize: item.droplet_size,
|
||||
sprayPattern: item.spray_pattern,
|
||||
createdAt: item.created_at
|
||||
}))
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route POST /api/nozzles/equipment/:equipmentId/assignments
|
||||
// @desc Assign nozzle to equipment
|
||||
// @access Private
|
||||
router.post('/equipment/:equipmentId/assignments', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const equipmentId = req.params.equipmentId;
|
||||
const { userNozzleId, position, quantityAssigned } = req.body;
|
||||
|
||||
// Verify equipment belongs to user
|
||||
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);
|
||||
}
|
||||
|
||||
// Verify nozzle belongs to user
|
||||
const nozzleCheck = await pool.query(
|
||||
'SELECT id, quantity FROM user_nozzles WHERE id = $1 AND user_id = $2',
|
||||
[userNozzleId, req.user.id]
|
||||
);
|
||||
|
||||
if (nozzleCheck.rows.length === 0) {
|
||||
throw new AppError('Nozzle not found', 404);
|
||||
}
|
||||
|
||||
const nozzle = nozzleCheck.rows[0];
|
||||
|
||||
// Check if enough nozzles are available
|
||||
const assignedCount = await pool.query(
|
||||
'SELECT COALESCE(SUM(quantity_assigned), 0) as total_assigned FROM equipment_nozzle_assignments WHERE user_nozzle_id = $1',
|
||||
[userNozzleId]
|
||||
);
|
||||
|
||||
const totalAssigned = parseInt(assignedCount.rows[0].total_assigned);
|
||||
const requestedQuantity = quantityAssigned || 1;
|
||||
|
||||
if (totalAssigned + requestedQuantity > nozzle.quantity) {
|
||||
throw new AppError(`Not enough nozzles available. Available: ${nozzle.quantity - totalAssigned}, Requested: ${requestedQuantity}`, 400);
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`INSERT INTO equipment_nozzle_assignments
|
||||
(user_equipment_id, user_nozzle_id, position, quantity_assigned)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *`,
|
||||
[equipmentId, userNozzleId, position || 'center', requestedQuantity]
|
||||
);
|
||||
|
||||
const assignment = result.rows[0];
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Nozzle assigned to equipment successfully',
|
||||
data: {
|
||||
assignment: {
|
||||
id: assignment.id,
|
||||
userEquipmentId: assignment.user_equipment_id,
|
||||
userNozzleId: assignment.user_nozzle_id,
|
||||
position: assignment.position,
|
||||
quantityAssigned: assignment.quantity_assigned,
|
||||
assignedDate: assignment.assigned_date,
|
||||
createdAt: assignment.created_at
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route DELETE /api/nozzles/assignments/:assignmentId
|
||||
// @desc Remove nozzle assignment from equipment
|
||||
// @access Private
|
||||
router.delete('/assignments/:assignmentId', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const assignmentId = req.params.assignmentId;
|
||||
|
||||
// Verify assignment belongs to user's equipment
|
||||
const checkResult = await pool.query(
|
||||
`SELECT ena.id FROM equipment_nozzle_assignments ena
|
||||
JOIN user_equipment ue ON ena.user_equipment_id = ue.id
|
||||
WHERE ena.id = $1 AND ue.user_id = $2`,
|
||||
[assignmentId, req.user.id]
|
||||
);
|
||||
|
||||
if (checkResult.rows.length === 0) {
|
||||
throw new AppError('Assignment not found', 404);
|
||||
}
|
||||
|
||||
await pool.query('DELETE FROM equipment_nozzle_assignments WHERE id = $1', [assignmentId]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Nozzle assignment removed successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user