From c65f9020a2971156ad8c68079b792f7190467ac1 Mon Sep 17 00:00:00 2001 From: Jake Kasper Date: Mon, 25 Aug 2025 10:01:55 -0400 Subject: [PATCH] fix some stuff --- backend/src/routes/productSpreaderSettings.js | 23 ++- frontend/src/pages/Products/Products.js | 172 ++++++++++++++++-- 2 files changed, 175 insertions(+), 20 deletions(-) diff --git a/backend/src/routes/productSpreaderSettings.js b/backend/src/routes/productSpreaderSettings.js index e36cd24..010694f 100644 --- a/backend/src/routes/productSpreaderSettings.js +++ b/backend/src/routes/productSpreaderSettings.js @@ -181,7 +181,7 @@ router.post('/', validateRequest(spreaderSettingSchema), async (req, res, next) router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettingSchema), async (req, res, next) => { try { const settingId = req.params.id; - const { productId, userProductId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes } = req.body; + const { productId, userProductId, equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes } = req.body; // Check if setting exists and user has permission to edit it let checkQuery; @@ -206,13 +206,25 @@ router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettin 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 spreader_brand = $1, spreader_model = $2, setting_value = $3, - rate_description = $4, notes = $5 - WHERE id = $6 + SET equipment_id = $1, spreader_brand = $2, spreader_model = $3, setting_value = $4, + rate_description = $5, notes = $6 + WHERE id = $7 RETURNING *`, - [spreaderBrand, spreaderModel, settingValue, rateDescription, notes, settingId] + [equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes, settingId] ); const setting = result.rows[0]; @@ -223,6 +235,7 @@ router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettin data: { setting: { id: setting.id, + equipmentId: setting.equipment_id, spreaderBrand: setting.spreader_brand, spreaderModel: setting.spreader_model, settingValue: setting.setting_value, diff --git a/frontend/src/pages/Products/Products.js b/frontend/src/pages/Products/Products.js index ff5f384..4196804 100644 --- a/frontend/src/pages/Products/Products.js +++ b/frontend/src/pages/Products/Products.js @@ -502,6 +502,12 @@ const CreateProductModal = ({ onSubmit, onCancel, sharedProducts, categories }) customRateAmount: formData.customRateAmount ? parseFloat(formData.customRateAmount) : null, customRateUnit: formData.customRateUnit || null, notes: formData.notes || null, + // Advanced fields for custom products + brand: formData.brand || null, + categoryId: formData.categoryId ? parseInt(formData.categoryId) : null, + productType: formData.productType || null, + activeIngredients: formData.activeIngredients || null, + description: formData.description || null, spreaderSettings: formData.productType === 'granular' ? spreaderSettings : [] }; @@ -790,6 +796,7 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor const [editSpreaderSettings, setEditSpreaderSettings] = useState([]); const [availableEditSpreaders, setAvailableEditSpreaders] = useState([]); const [loadingEditSpreaders, setLoadingEditSpreaders] = useState(false); + const [editingSettingIndex, setEditingSettingIndex] = useState(null); const [newEditSpreaderSetting, setNewEditSpreaderSetting] = useState({ equipmentId: '', settingValue: '', @@ -829,7 +836,7 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor }); if (response.ok) { const data = await response.json(); - setEditSpreaderSettings(data.data || []); + setEditSpreaderSettings(data.data?.settings || []); } } catch (error) { console.error('Failed to load spreader settings:', error); @@ -936,6 +943,123 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor setEditSpreaderSettings(updated); }; + const handleStartEditSetting = (index) => { + setEditingSettingIndex(index); + }; + + const handleCancelEditSetting = () => { + setEditingSettingIndex(null); + }; + + const handleSaveEditSetting = (index, updatedSetting) => { + const updated = [...editSpreaderSettings]; + updated[index] = updatedSetting; + setEditSpreaderSettings(updated); + setEditingSettingIndex(null); + }; + + // Inline edit component for existing spreader settings + const EditSpreaderSettingForm = ({ setting, availableSpreaders, onSave, onCancel }) => { + const [editValues, setEditValues] = useState({ + equipmentId: setting.equipmentId || '', + settingValue: setting.settingValue || '', + rateDescription: setting.rateDescription || '', + notes: setting.notes || '' + }); + + const handleSave = () => { + if (!editValues.settingValue.trim()) { + return; + } + + const selectedSpreader = availableSpreaders.find(s => s.id.toString() === editValues.equipmentId); + + const updatedSetting = { + ...setting, + equipmentId: editValues.equipmentId ? parseInt(editValues.equipmentId) : null, + equipmentName: selectedSpreader?.name || setting.equipmentName, + equipmentManufacturer: selectedSpreader?.manufacturer || setting.equipmentManufacturer, + equipmentModel: selectedSpreader?.model || setting.equipmentModel, + settingValue: editValues.settingValue.trim(), + rateDescription: editValues.rateDescription.trim() || null, + notes: editValues.notes.trim() || null + }; + + onSave(updatedSetting); + }; + + return ( +
+
+ + +
+ +
+ + setEditValues({ ...editValues, settingValue: e.target.value })} + className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" + placeholder="e.g., 3.5, H, 15" + /> +
+ +
+ + setEditValues({ ...editValues, rateDescription: e.target.value })} + className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" + placeholder="e.g., 1 lb nitrogen per 1000 sq ft" + /> +
+ +
+ + setEditValues({ ...editValues, notes: e.target.value })} + className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" + placeholder="Additional notes..." + /> +
+ +
+ + +
+
+ ); + }; + return (
@@ -1172,21 +1296,39 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor

Current Settings:

{editSpreaderSettings.map((setting, index) => (
-
-
-
{setting.equipmentName || `${setting.spreaderBrand}${setting.spreaderModel ? ` (${setting.spreaderModel})` : ''}`}
-
Setting: {setting.settingValue}
- {setting.rateDescription &&
{setting.rateDescription}
} - {setting.notes &&
{setting.notes}
} + {editingSettingIndex === index ? ( + handleSaveEditSetting(index, updatedSetting)} + onCancel={handleCancelEditSetting} + /> + ) : ( +
+
+
{setting.equipmentName || `${setting.spreaderBrand}${setting.spreaderModel ? ` (${setting.spreaderModel})` : ''}`}
+
Setting: {setting.settingValue}
+ {setting.rateDescription &&
{setting.rateDescription}
} + {setting.notes &&
{setting.notes}
} +
+
+ + +
- -
+ )}
))}