admin
This commit is contained in:
@@ -526,4 +526,300 @@ router.get('/system/health', async (req, res, next) => {
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET /api/admin/products/user
|
||||
// @desc Get all user products for admin management
|
||||
// @access Private (Admin)
|
||||
router.get('/products/user', async (req, res, next) => {
|
||||
try {
|
||||
const { search, category, user_id } = req.query;
|
||||
|
||||
let whereConditions = [];
|
||||
let queryParams = [];
|
||||
let paramCount = 0;
|
||||
|
||||
if (search) {
|
||||
paramCount++;
|
||||
whereConditions.push(`(up.custom_name ILIKE $${paramCount} OR up.custom_brand ILIKE $${paramCount} OR u.email ILIKE $${paramCount})`);
|
||||
queryParams.push(`%${search}%`);
|
||||
}
|
||||
|
||||
if (category) {
|
||||
paramCount++;
|
||||
whereConditions.push(`up.category_id = $${paramCount}`);
|
||||
queryParams.push(category);
|
||||
}
|
||||
|
||||
if (user_id) {
|
||||
paramCount++;
|
||||
whereConditions.push(`up.user_id = $${paramCount}`);
|
||||
queryParams.push(user_id);
|
||||
}
|
||||
|
||||
const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
|
||||
|
||||
const result = await pool.query(`
|
||||
SELECT up.*, u.email as user_email, u.first_name, u.last_name,
|
||||
pc.name as category_name, p.name as base_product_name, p.brand as base_product_brand,
|
||||
COUNT(pss.id) as spreader_settings_count,
|
||||
COUNT(DISTINCT app.id) as usage_count
|
||||
FROM user_products up
|
||||
JOIN users u ON up.user_id = u.id
|
||||
LEFT JOIN product_categories pc ON up.category_id = pc.id
|
||||
LEFT JOIN products p ON up.product_id = p.id
|
||||
LEFT JOIN product_spreader_settings pss ON up.id = pss.user_product_id
|
||||
LEFT JOIN application_plan_products app ON up.id = app.user_product_id
|
||||
${whereClause}
|
||||
GROUP BY up.id, u.email, u.first_name, u.last_name, pc.name, p.name, p.brand
|
||||
ORDER BY u.email, up.custom_name
|
||||
`, queryParams);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
userProducts: result.rows.map(product => ({
|
||||
id: product.id,
|
||||
userId: product.user_id,
|
||||
userEmail: product.user_email,
|
||||
userName: `${product.first_name} ${product.last_name}`,
|
||||
baseProductId: product.product_id,
|
||||
baseProductName: product.base_product_name,
|
||||
baseProductBrand: product.base_product_brand,
|
||||
customName: product.custom_name,
|
||||
customBrand: product.custom_brand,
|
||||
categoryId: product.category_id,
|
||||
categoryName: product.category_name,
|
||||
productType: product.custom_product_type,
|
||||
activeIngredients: product.custom_active_ingredients,
|
||||
description: product.custom_description,
|
||||
rateAmount: product.custom_rate_amount,
|
||||
rateUnit: product.custom_rate_unit,
|
||||
notes: product.notes,
|
||||
spreaderSettingsCount: parseInt(product.spreader_settings_count),
|
||||
usageCount: parseInt(product.usage_count),
|
||||
createdAt: product.created_at,
|
||||
updatedAt: product.updated_at
|
||||
}))
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route POST /api/admin/products/user/:id/promote
|
||||
// @desc Promote user product to shared product
|
||||
// @access Private (Admin)
|
||||
router.post('/products/user/:id/promote', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const userProductId = req.params.id;
|
||||
|
||||
// Get the user product
|
||||
const userProductResult = await pool.query(
|
||||
`SELECT up.*, pc.name as category_name
|
||||
FROM user_products up
|
||||
LEFT JOIN product_categories pc ON up.category_id = pc.id
|
||||
WHERE up.id = $1`,
|
||||
[userProductId]
|
||||
);
|
||||
|
||||
if (userProductResult.rows.length === 0) {
|
||||
throw new AppError('User product not found', 404);
|
||||
}
|
||||
|
||||
const userProduct = userProductResult.rows[0];
|
||||
|
||||
// Create new shared product
|
||||
const newProductResult = await pool.query(`
|
||||
INSERT INTO products (name, brand, category_id, product_type, active_ingredients, description)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING *
|
||||
`, [
|
||||
userProduct.custom_name,
|
||||
userProduct.custom_brand || 'Generic',
|
||||
userProduct.category_id,
|
||||
userProduct.custom_product_type,
|
||||
userProduct.custom_active_ingredients,
|
||||
userProduct.custom_description
|
||||
]);
|
||||
|
||||
const newProduct = newProductResult.rows[0];
|
||||
|
||||
// Create default rate if custom rate exists
|
||||
if (userProduct.custom_rate_amount && userProduct.custom_rate_unit) {
|
||||
await pool.query(`
|
||||
INSERT INTO product_rates (product_id, application_type, rate_amount, rate_unit, notes)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
`, [
|
||||
newProduct.id,
|
||||
userProduct.custom_product_type,
|
||||
userProduct.custom_rate_amount,
|
||||
userProduct.custom_rate_unit,
|
||||
`Promoted from user product: ${userProduct.notes || ''}`
|
||||
]);
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `User product "${userProduct.custom_name}" promoted to shared product`,
|
||||
data: {
|
||||
newProduct: {
|
||||
id: newProduct.id,
|
||||
name: newProduct.name,
|
||||
brand: newProduct.brand,
|
||||
categoryId: newProduct.category_id,
|
||||
productType: newProduct.product_type,
|
||||
activeIngredients: newProduct.active_ingredients,
|
||||
description: newProduct.description
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET /api/admin/equipment/user
|
||||
// @desc Get all user equipment for admin management
|
||||
// @access Private (Admin)
|
||||
router.get('/equipment/user', async (req, res, next) => {
|
||||
try {
|
||||
const { search, category, user_id } = req.query;
|
||||
|
||||
let whereConditions = [];
|
||||
let queryParams = [];
|
||||
let paramCount = 0;
|
||||
|
||||
if (search) {
|
||||
paramCount++;
|
||||
whereConditions.push(`(ue.custom_name ILIKE $${paramCount} OR ue.manufacturer ILIKE $${paramCount} OR u.email ILIKE $${paramCount})`);
|
||||
queryParams.push(`%${search}%`);
|
||||
}
|
||||
|
||||
if (category) {
|
||||
paramCount++;
|
||||
whereConditions.push(`ue.category_id = $${paramCount}`);
|
||||
queryParams.push(category);
|
||||
}
|
||||
|
||||
if (user_id) {
|
||||
paramCount++;
|
||||
whereConditions.push(`ue.user_id = $${paramCount}`);
|
||||
queryParams.push(user_id);
|
||||
}
|
||||
|
||||
const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
|
||||
|
||||
const result = await pool.query(`
|
||||
SELECT ue.*, u.email as user_email, u.first_name, u.last_name,
|
||||
ec.name as category_name, et.name as type_name, et.manufacturer as type_manufacturer, et.model as type_model,
|
||||
COUNT(DISTINCT nc.id) as nozzle_configurations,
|
||||
COUNT(DISTINCT app.id) as usage_count
|
||||
FROM user_equipment ue
|
||||
JOIN users u ON ue.user_id = u.id
|
||||
LEFT JOIN equipment_categories ec ON ue.category_id = ec.id
|
||||
LEFT JOIN equipment_types et ON ue.equipment_type_id = et.id
|
||||
LEFT JOIN nozzle_configurations nc ON ue.id = nc.sprayer_id
|
||||
LEFT JOIN application_plans app ON ue.id = app.equipment_id
|
||||
${whereClause}
|
||||
GROUP BY ue.id, u.email, u.first_name, u.last_name, ec.name, et.name, et.manufacturer, et.model
|
||||
ORDER BY u.email, ue.custom_name
|
||||
`, queryParams);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
userEquipment: result.rows.map(equipment => ({
|
||||
id: equipment.id,
|
||||
userId: equipment.user_id,
|
||||
userEmail: equipment.user_email,
|
||||
userName: `${equipment.first_name} ${equipment.last_name}`,
|
||||
equipmentTypeId: equipment.equipment_type_id,
|
||||
categoryId: equipment.category_id,
|
||||
typeName: equipment.type_name,
|
||||
typeManufacturer: equipment.type_manufacturer,
|
||||
typeModel: equipment.type_model,
|
||||
categoryName: equipment.category_name,
|
||||
customName: equipment.custom_name,
|
||||
manufacturer: equipment.manufacturer,
|
||||
model: equipment.model,
|
||||
// Spreader fields
|
||||
capacityLbs: parseFloat(equipment.capacity_lbs) || null,
|
||||
spreaderType: equipment.spreader_type,
|
||||
spreadWidth: parseFloat(equipment.spread_width) || null,
|
||||
// Sprayer fields
|
||||
tankCapacityGallons: parseFloat(equipment.tank_capacity_gallons) || null,
|
||||
sprayerType: equipment.sprayer_type,
|
||||
boomWidth: parseFloat(equipment.boom_width) || null,
|
||||
numberOfNozzles: parseInt(equipment.number_of_nozzles) || null,
|
||||
// Common fields
|
||||
yearManufactured: parseInt(equipment.year_manufactured) || null,
|
||||
serialNumber: equipment.serial_number,
|
||||
notes: equipment.notes,
|
||||
isActive: equipment.is_active,
|
||||
nozzleConfigurations: parseInt(equipment.nozzle_configurations),
|
||||
usageCount: parseInt(equipment.usage_count),
|
||||
createdAt: equipment.created_at,
|
||||
updatedAt: equipment.updated_at
|
||||
}))
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route POST /api/admin/equipment/user/:id/promote
|
||||
// @desc Promote user equipment to shared equipment type
|
||||
// @access Private (Admin)
|
||||
router.post('/equipment/user/:id/promote', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const userEquipmentId = req.params.id;
|
||||
|
||||
// Get the user equipment
|
||||
const userEquipmentResult = await pool.query(
|
||||
`SELECT ue.*, ec.name as category_name
|
||||
FROM user_equipment ue
|
||||
LEFT JOIN equipment_categories ec ON ue.category_id = ec.id
|
||||
WHERE ue.id = $1`,
|
||||
[userEquipmentId]
|
||||
);
|
||||
|
||||
if (userEquipmentResult.rows.length === 0) {
|
||||
throw new AppError('User equipment not found', 404);
|
||||
}
|
||||
|
||||
const userEquipment = userEquipmentResult.rows[0];
|
||||
|
||||
// Create new shared equipment type
|
||||
const newEquipmentTypeResult = await pool.query(`
|
||||
INSERT INTO equipment_types (name, manufacturer, model, category_id)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *
|
||||
`, [
|
||||
userEquipment.custom_name,
|
||||
userEquipment.manufacturer || 'Generic',
|
||||
userEquipment.model || 'Standard',
|
||||
userEquipment.category_id
|
||||
]);
|
||||
|
||||
const newEquipmentType = newEquipmentTypeResult.rows[0];
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `User equipment "${userEquipment.custom_name}" promoted to shared equipment type`,
|
||||
data: {
|
||||
newEquipmentType: {
|
||||
id: newEquipmentType.id,
|
||||
name: newEquipmentType.name,
|
||||
manufacturer: newEquipmentType.manufacturer,
|
||||
model: newEquipmentType.model,
|
||||
categoryId: newEquipmentType.category_id
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user