diff --git a/backend/src/routes/admin.js b/backend/src/routes/admin.js index 682d364..2bb9140 100644 --- a/backend/src/routes/admin.js +++ b/backend/src/routes/admin.js @@ -711,17 +711,12 @@ router.get('/equipment/user', async (req, res, next) => { 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 + ec.name as category_name, et.name as type_name, et.manufacturer as type_manufacturer, et.model as type_model 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); @@ -756,8 +751,6 @@ router.get('/equipment/user', async (req, res, next) => { 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 })) @@ -822,4 +815,80 @@ router.post('/equipment/user/:id/promote', validateParams(idParamSchema), async } }); +// @route GET /api/admin/products/:id/rates +// @desc Get application rates for a specific shared product +// @access Private (Admin) +router.get('/products/:id/rates', validateParams(idParamSchema), async (req, res, next) => { + try { + const productId = req.params.id; + + const result = await pool.query(` + SELECT pr.*, p.name as product_name, p.brand as product_brand + FROM product_rates pr + JOIN products p ON pr.product_id = p.id + WHERE pr.product_id = $1 + ORDER BY pr.application_type, pr.rate_amount + `, [productId]); + + res.json({ + success: true, + data: { + rates: result.rows.map(rate => ({ + id: rate.id, + productId: rate.product_id, + productName: rate.product_name, + productBrand: rate.product_brand, + applicationType: rate.application_type, + rateAmount: parseFloat(rate.rate_amount), + rateUnit: rate.rate_unit, + notes: rate.notes, + createdAt: rate.created_at + })) + } + }); + } catch (error) { + next(error); + } +}); + +// @route GET /api/admin/products/user/:id/spreader-settings +// @desc Get spreader settings for a specific user product +// @access Private (Admin) +router.get('/products/user/:id/spreader-settings', validateParams(idParamSchema), async (req, res, next) => { + try { + const userProductId = req.params.id; + + const result = await pool.query(` + SELECT pss.*, ue.custom_name as equipment_name, ss.brand_name, ss.model, ss.setting_number + FROM product_spreader_settings pss + JOIN user_equipment ue ON pss.user_equipment_id = ue.id + LEFT JOIN spreader_settings ss ON pss.spreader_setting_id = ss.id + WHERE pss.user_product_id = $1 + ORDER BY ue.custom_name, ss.brand_name + `, [userProductId]); + + res.json({ + success: true, + data: { + spreaderSettings: result.rows.map(setting => ({ + id: setting.id, + userProductId: setting.user_product_id, + userEquipmentId: setting.user_equipment_id, + equipmentName: setting.equipment_name, + spreaderSettingId: setting.spreader_setting_id, + brandName: setting.brand_name, + model: setting.model, + settingNumber: setting.setting_number, + customRate: setting.custom_rate ? parseFloat(setting.custom_rate) : null, + customRateUnit: setting.custom_rate_unit, + notes: setting.notes, + createdAt: setting.created_at + })) + } + }); + } catch (error) { + next(error); + } +}); + module.exports = router; \ No newline at end of file diff --git a/frontend/src/pages/Admin/AdminProducts.js b/frontend/src/pages/Admin/AdminProducts.js index 43ec220..e8d4f16 100644 --- a/frontend/src/pages/Admin/AdminProducts.js +++ b/frontend/src/pages/Admin/AdminProducts.js @@ -7,7 +7,8 @@ import { TrashIcon, ExclamationTriangleIcon, TagIcon, - ArrowUpIcon + ArrowUpIcon, + InformationCircleIcon } from '@heroicons/react/24/outline'; import { adminAPI, productsAPI } from '../../services/api'; import LoadingSpinner from '../../components/UI/LoadingSpinner'; @@ -26,6 +27,8 @@ const AdminProducts = () => { const [showCreateModal, setShowCreateModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showDetailsModal, setShowDetailsModal] = useState(false); + const [productDetails, setProductDetails] = useState({ rates: [], spreaderSettings: [] }); const [formData, setFormData] = useState({ name: '', brand: '', @@ -123,6 +126,30 @@ const AdminProducts = () => { } }; + const showProductDetails = async (product) => { + try { + setSelectedProduct(product); + let rates = []; + let spreaderSettings = []; + + if (product.isShared) { + // Fetch application rates for shared products + const ratesResponse = await adminAPI.getProductRates(product.id); + rates = ratesResponse.data.data.rates || []; + } else { + // Fetch spreader settings for user products + const settingsResponse = await adminAPI.getUserProductSpreaderSettings(product.id); + spreaderSettings = settingsResponse.data.data.spreaderSettings || []; + } + + setProductDetails({ rates, spreaderSettings }); + setShowDetailsModal(true); + } catch (error) { + console.error('Failed to fetch product details:', error); + toast.error('Failed to load product details'); + } + }; + const resetForm = () => { setFormData({ name: '', @@ -508,6 +535,13 @@ const AdminProducts = () => {
| Type | +Rate | +Unit | +Notes | +
|---|---|---|---|
| + {rate.applicationType} + | ++ {rate.rateAmount} + | ++ {rate.rateUnit} + | ++ {rate.notes || 'No notes'} + | +
No application rates defined for this product.
+ )} +| Equipment | +Spreader | +Setting | +Custom Rate | +Notes | +
|---|---|---|---|---|
| + {setting.equipmentName} + | ++ {setting.brandName} {setting.model} + | ++ {setting.settingNumber || 'N/A'} + | ++ {setting.customRate ? `${setting.customRate} ${setting.customRateUnit}` : 'Standard'} + | ++ {setting.notes || 'No notes'} + | +
No spreader settings configured for this product.
+ )} +