diff --git a/backend/scripts/fix-categories-safe.sql b/backend/scripts/fix-categories-safe.sql new file mode 100644 index 0000000..c71a920 --- /dev/null +++ b/backend/scripts/fix-categories-safe.sql @@ -0,0 +1,60 @@ +-- Safe category cleanup that handles existing singular forms +-- This will consolidate similar categories and fix naming + +-- First, update any products that reference plural categories to point to singular ones +-- Update any references to 'Fertilizers' to point to 'Fertilizer' (if it exists) +UPDATE products SET category_id = ( + SELECT id FROM product_categories WHERE name = 'Fertilizer' LIMIT 1 +) WHERE category_id = ( + SELECT id FROM product_categories WHERE name = 'Fertilizers' LIMIT 1 +) AND EXISTS (SELECT 1 FROM product_categories WHERE name = 'Fertilizer'); + +-- Update any references to 'Herbicides' to point to 'Herbicide' (if it exists) +UPDATE products SET category_id = ( + SELECT id FROM product_categories WHERE name = 'Herbicide' LIMIT 1 +) WHERE category_id = ( + SELECT id FROM product_categories WHERE name = 'Herbicides' LIMIT 1 +) AND EXISTS (SELECT 1 FROM product_categories WHERE name = 'Herbicide'); + +-- Update any references to 'Fungicides' to point to 'Fungicide' (if it exists) +UPDATE products SET category_id = ( + SELECT id FROM product_categories WHERE name = 'Fungicide' LIMIT 1 +) WHERE category_id = ( + SELECT id FROM product_categories WHERE name = 'Fungicides' LIMIT 1 +) AND EXISTS (SELECT 1 FROM product_categories WHERE name = 'Fungicide'); + +-- Update any references to 'Insecticides' to point to 'Insecticide' (if it exists) +UPDATE products SET category_id = ( + SELECT id FROM product_categories WHERE name = 'Insecticide' LIMIT 1 +) WHERE category_id = ( + SELECT id FROM product_categories WHERE name = 'Insecticides' LIMIT 1 +) AND EXISTS (SELECT 1 FROM product_categories WHERE name = 'Insecticide'); + +-- Delete plural categories that now have singular equivalents +DELETE FROM product_categories WHERE name IN ('Fertilizers', 'Herbicides', 'Fungicides', 'Insecticides') +AND EXISTS (SELECT 1 FROM product_categories WHERE name IN ('Fertilizer', 'Herbicide', 'Fungicide', 'Insecticide')); + +-- Update remaining plural categories to singular (only if singular doesn't exist) +UPDATE product_categories SET name = 'Surfactant' WHERE name = 'Surfactants' +AND NOT EXISTS (SELECT 1 FROM product_categories WHERE name = 'Surfactant'); + +UPDATE product_categories SET name = 'Adjuvant' WHERE name = 'Adjuvants' +AND NOT EXISTS (SELECT 1 FROM product_categories WHERE name = 'Adjuvant'); + +UPDATE product_categories SET name = 'Growth Regulator' WHERE name = 'Growth Regulators' +AND NOT EXISTS (SELECT 1 FROM product_categories WHERE name = 'Growth Regulator'); + +-- Add any missing core categories (only if they don't exist) +INSERT INTO product_categories (name, description) VALUES +('Herbicide', 'Products for weed control and prevention'), +('Fertilizer', 'Nutrients for lawn growth and health'), +('Fungicide', 'Products for disease prevention and treatment'), +('Insecticide', 'Products for insect control'), +('Pre-emergent', 'Products that prevent weeds from germinating'), +('Post-emergent', 'Products that kill existing weeds'), +('Growth Regulator', 'Products that modify plant growth'), +('Surfactant', 'Products that improve spray coverage and penetration'), +('Adjuvant', 'Products that enhance pesticide performance'), +('Seed', 'Grass seeds and seed treatments'), +('Soil Amendment', 'Products that improve soil conditions') +ON CONFLICT (name) DO NOTHING; \ No newline at end of file diff --git a/backend/src/utils/validation.js b/backend/src/utils/validation.js index 700ed1a..7a5692e 100644 --- a/backend/src/utils/validation.js +++ b/backend/src/utils/validation.js @@ -80,7 +80,14 @@ const userProductSchema = Joi.object({ customName: Joi.string().max(255).allow(null).optional(), customRateAmount: Joi.number().positive().allow(null).optional(), customRateUnit: Joi.string().max(50).allow(null).optional(), - notes: Joi.string().allow(null, '').optional() + notes: Joi.string().allow(null, '').optional(), + // Additional fields for advanced editing + brand: Joi.string().max(100).allow(null).optional(), + categoryId: Joi.number().integer().positive().allow(null).optional(), + productType: Joi.string().valid('granular', 'liquid', 'seed', 'powder').allow(null).optional(), + activeIngredients: Joi.string().allow(null).optional(), + description: Joi.string().allow(null).optional(), + isAdvancedEdit: Joi.boolean().optional() }); // Application validation schemas diff --git a/frontend/src/pages/Products/Products.js b/frontend/src/pages/Products/Products.js index 1a66a8e..936f901 100644 --- a/frontend/src/pages/Products/Products.js +++ b/frontend/src/pages/Products/Products.js @@ -583,26 +583,68 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor customName: product.customName || '', customRateAmount: product.customRateAmount || '', customRateUnit: product.customRateUnit || 'lbs/1000 sq ft', - notes: product.notes || '' + notes: product.notes || '', + // Add fields for full editing capability + brand: product.brand || '', + categoryId: product.categoryId || '', + productType: product.productType || '', + activeIngredients: product.activeIngredients || '', + description: product.description || '' }); + const [editMode, setEditMode] = useState('basic'); // 'basic' or 'advanced' + const handleSubmit = (e) => { e.preventDefault(); - if (!formData.productId && !formData.customName) { - toast.error('Please select a base product or enter a custom name'); - return; + if (editMode === 'basic') { + // Basic mode validation + if (!formData.productId && !formData.customName) { + toast.error('Please select a base product or enter a custom name'); + return; + } + + const submitData = { + productId: formData.productId ? parseInt(formData.productId) : null, + customName: formData.customName || null, + customRateAmount: formData.customRateAmount ? parseFloat(formData.customRateAmount) : null, + customRateUnit: formData.customRateUnit || null, + notes: formData.notes || null + }; + + onSubmit(submitData); + } else { + // Advanced mode validation and submission + if (!formData.customName) { + toast.error('Product name is required'); + return; + } + if (!formData.categoryId) { + toast.error('Category is required'); + return; + } + if (!formData.productType) { + toast.error('Product type is required'); + return; + } + + const submitData = { + productId: formData.productId ? parseInt(formData.productId) : null, + customName: formData.customName, + customRateAmount: formData.customRateAmount ? parseFloat(formData.customRateAmount) : null, + customRateUnit: formData.customRateUnit || null, + notes: formData.notes || null, + // Additional fields for advanced mode + brand: formData.brand || null, + categoryId: formData.categoryId ? parseInt(formData.categoryId) : null, + productType: formData.productType || null, + activeIngredients: formData.activeIngredients || null, + description: formData.description || null, + isAdvancedEdit: true // Flag to indicate this is an advanced edit + }; + + onSubmit(submitData); } - - const submitData = { - productId: formData.productId ? parseInt(formData.productId) : null, - customName: formData.customName || null, - customRateAmount: formData.customRateAmount ? parseFloat(formData.customRateAmount) : null, - customRateUnit: formData.customRateUnit || null, - notes: formData.notes || null - }; - - onSubmit(submitData); }; return ( @@ -610,73 +652,228 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor