fix some stuff

This commit is contained in:
Jake Kasper
2025-08-25 10:01:55 -04:00
parent d9ab650d39
commit c65f9020a2
2 changed files with 175 additions and 20 deletions

View File

@@ -181,7 +181,7 @@ router.post('/', validateRequest(spreaderSettingSchema), async (req, res, next)
router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettingSchema), async (req, res, next) => { router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettingSchema), async (req, res, next) => {
try { try {
const settingId = req.params.id; 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 // Check if setting exists and user has permission to edit it
let checkQuery; let checkQuery;
@@ -206,13 +206,25 @@ router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettin
throw new AppError('Spreader setting not found', 404); 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( const result = await pool.query(
`UPDATE product_spreader_settings `UPDATE product_spreader_settings
SET spreader_brand = $1, spreader_model = $2, setting_value = $3, SET equipment_id = $1, spreader_brand = $2, spreader_model = $3, setting_value = $4,
rate_description = $4, notes = $5 rate_description = $5, notes = $6
WHERE id = $6 WHERE id = $7
RETURNING *`, RETURNING *`,
[spreaderBrand, spreaderModel, settingValue, rateDescription, notes, settingId] [equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes, settingId]
); );
const setting = result.rows[0]; const setting = result.rows[0];
@@ -223,6 +235,7 @@ router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettin
data: { data: {
setting: { setting: {
id: setting.id, id: setting.id,
equipmentId: setting.equipment_id,
spreaderBrand: setting.spreader_brand, spreaderBrand: setting.spreader_brand,
spreaderModel: setting.spreader_model, spreaderModel: setting.spreader_model,
settingValue: setting.setting_value, settingValue: setting.setting_value,

View File

@@ -502,6 +502,12 @@ const CreateProductModal = ({ onSubmit, onCancel, sharedProducts, categories })
customRateAmount: formData.customRateAmount ? parseFloat(formData.customRateAmount) : null, customRateAmount: formData.customRateAmount ? parseFloat(formData.customRateAmount) : null,
customRateUnit: formData.customRateUnit || null, customRateUnit: formData.customRateUnit || null,
notes: formData.notes || 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 : [] spreaderSettings: formData.productType === 'granular' ? spreaderSettings : []
}; };
@@ -790,6 +796,7 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
const [editSpreaderSettings, setEditSpreaderSettings] = useState([]); const [editSpreaderSettings, setEditSpreaderSettings] = useState([]);
const [availableEditSpreaders, setAvailableEditSpreaders] = useState([]); const [availableEditSpreaders, setAvailableEditSpreaders] = useState([]);
const [loadingEditSpreaders, setLoadingEditSpreaders] = useState(false); const [loadingEditSpreaders, setLoadingEditSpreaders] = useState(false);
const [editingSettingIndex, setEditingSettingIndex] = useState(null);
const [newEditSpreaderSetting, setNewEditSpreaderSetting] = useState({ const [newEditSpreaderSetting, setNewEditSpreaderSetting] = useState({
equipmentId: '', equipmentId: '',
settingValue: '', settingValue: '',
@@ -829,7 +836,7 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
}); });
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
setEditSpreaderSettings(data.data || []); setEditSpreaderSettings(data.data?.settings || []);
} }
} catch (error) { } catch (error) {
console.error('Failed to load spreader settings:', error); console.error('Failed to load spreader settings:', error);
@@ -936,6 +943,123 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
setEditSpreaderSettings(updated); 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 (
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Spreader</label>
<select
value={editValues.equipmentId}
onChange={(e) => setEditValues({ ...editValues, equipmentId: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm"
>
<option value="">Select equipment...</option>
{availableSpreaders.map(spreader => (
<option key={spreader.id} value={spreader.id}>
{spreader.name} ({spreader.manufacturer} {spreader.model})
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Setting Value *</label>
<input
type="text"
value={editValues.settingValue}
onChange={(e) => 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"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Rate Description</label>
<input
type="text"
value={editValues.rateDescription}
onChange={(e) => 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"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Notes</label>
<input
type="text"
value={editValues.notes}
onChange={(e) => setEditValues({ ...editValues, notes: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm"
placeholder="Additional notes..."
/>
</div>
<div className="flex gap-2 pt-2">
<button
type="button"
onClick={handleSave}
disabled={!editValues.settingValue.trim()}
className="px-3 py-1 bg-blue-600 text-white rounded text-sm hover:bg-blue-700 disabled:bg-gray-300"
>
Save
</button>
<button
type="button"
onClick={onCancel}
className="px-3 py-1 bg-gray-500 text-white rounded text-sm hover:bg-gray-600"
>
Cancel
</button>
</div>
</div>
);
};
return ( return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-6 w-full max-w-lg max-h-[90vh] overflow-y-auto"> <div className="bg-white rounded-lg p-6 w-full max-w-lg max-h-[90vh] overflow-y-auto">
@@ -1172,21 +1296,39 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
<h4 className="text-sm font-medium text-gray-700 mb-2">Current Settings:</h4> <h4 className="text-sm font-medium text-gray-700 mb-2">Current Settings:</h4>
{editSpreaderSettings.map((setting, index) => ( {editSpreaderSettings.map((setting, index) => (
<div key={index} className="bg-gray-50 p-3 rounded border text-sm"> <div key={index} className="bg-gray-50 p-3 rounded border text-sm">
<div className="flex justify-between items-start"> {editingSettingIndex === index ? (
<div> <EditSpreaderSettingForm
<div><strong>{setting.equipmentName || `${setting.spreaderBrand}${setting.spreaderModel ? ` (${setting.spreaderModel})` : ''}`}</strong></div> setting={setting}
<div>Setting: <span className="font-medium">{setting.settingValue}</span></div> availableSpreaders={availableEditSpreaders}
{setting.rateDescription && <div className="text-gray-600">{setting.rateDescription}</div>} onSave={(updatedSetting) => handleSaveEditSetting(index, updatedSetting)}
{setting.notes && <div className="text-gray-600 italic">{setting.notes}</div>} onCancel={handleCancelEditSetting}
/>
) : (
<div className="flex justify-between items-start">
<div>
<div><strong>{setting.equipmentName || `${setting.spreaderBrand}${setting.spreaderModel ? ` (${setting.spreaderModel})` : ''}`}</strong></div>
<div>Setting: <span className="font-medium">{setting.settingValue}</span></div>
{setting.rateDescription && <div className="text-gray-600">{setting.rateDescription}</div>}
{setting.notes && <div className="text-gray-600 italic">{setting.notes}</div>}
</div>
<div className="flex gap-2">
<button
type="button"
onClick={() => handleStartEditSetting(index)}
className="text-blue-600 hover:text-blue-800 text-sm"
>
Edit
</button>
<button
type="button"
onClick={() => handleRemoveExistingSpreaderSetting(index)}
className="text-red-600 hover:text-red-800 text-sm"
>
Remove
</button>
</div>
</div> </div>
<button )}
type="button"
onClick={() => handleRemoveExistingSpreaderSetting(index)}
className="text-red-600 hover:text-red-800 text-sm"
>
Remove
</button>
</div>
</div> </div>
))} ))}
</div> </div>