seed updates
This commit is contained in:
@@ -218,34 +218,105 @@ const Products = () => {
|
||||
</p>
|
||||
)}
|
||||
|
||||
{product.activeIngredients && (
|
||||
{/* Seed blend pretty display */}
|
||||
{product.productType === 'seed' ? (
|
||||
(()=>{
|
||||
let blend = [];
|
||||
// Shared product
|
||||
if (Array.isArray(product.seedBlend) && product.seedBlend.length) {
|
||||
blend = product.seedBlend;
|
||||
} else if (product.activeIngredients) {
|
||||
// User product stores blend in activeIngredients JSON
|
||||
try {
|
||||
const ai = typeof product.activeIngredients === 'string' ? JSON.parse(product.activeIngredients) : product.activeIngredients;
|
||||
blend = ai?.seedBlend || [];
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<p className="text-sm font-medium text-gray-900">Seed Blend:</p>
|
||||
{blend.length === 0 ? (
|
||||
<p className="text-sm text-gray-600">No blend details</p>
|
||||
) : (
|
||||
<div className="mt-1 flex flex-wrap gap-2">
|
||||
{blend.map((b, idx)=> (
|
||||
<span key={idx} className="px-2 py-1 rounded bg-gray-100 text-gray-800 text-xs">
|
||||
{b.cultivar} — {parseFloat(b.percent || 0).toFixed(1)}%
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})()
|
||||
) : (
|
||||
product.activeIngredients && (
|
||||
<p className="text-sm text-gray-700 mb-3">
|
||||
<strong>Active Ingredients:</strong> {product.activeIngredients}
|
||||
</p>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* Application Rates */}
|
||||
{isUserProduct && product.customRateAmount && (
|
||||
{isUserProduct && (
|
||||
(()=>{
|
||||
if (product.productType === 'seed') {
|
||||
try {
|
||||
const ai = typeof product.activeIngredients === 'string' ? JSON.parse(product.activeIngredients) : product.activeIngredients;
|
||||
const sr = ai?.seedRates;
|
||||
if (sr?.new || sr?.overseed) {
|
||||
return (
|
||||
<div className="bg-gray-50 p-3 rounded-lg mb-3">
|
||||
<p className="text-sm font-medium text-gray-900 mb-1">Your Rates:</p>
|
||||
{sr.new && <p className="text-sm text-gray-700">New Lawn: {sr.new} {sr.unit || product.customRateUnit}</p>}
|
||||
{sr.overseed && <p className="text-sm text-gray-700">Overseeding: {sr.overseed} {sr.unit || product.customRateUnit}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
if (product.customRateAmount) {
|
||||
return (
|
||||
<div className="bg-gray-50 p-3 rounded-lg mb-3">
|
||||
<p className="text-sm font-medium text-gray-900">Your Rate:</p>
|
||||
<p className="text-sm text-gray-700">
|
||||
{product.customRateAmount} {product.customRateUnit}
|
||||
</p>
|
||||
<p className="text-sm text-gray-700">{product.customRateAmount} {product.customRateUnit}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})()
|
||||
)}
|
||||
|
||||
{!isUserProduct && product.rates && product.rates.length > 0 && (
|
||||
<div className="bg-gray-50 p-3 rounded-lg mb-3">
|
||||
<p className="text-sm font-medium text-gray-900 mb-2">Application Rates:</p>
|
||||
{product.rates.slice(0, 2).map((rate, index) => (
|
||||
{product.productType === 'seed' ? (
|
||||
(()=>{
|
||||
const newRate = product.rates.find(r=> (r.applicationType||'').toLowerCase().includes('new'));
|
||||
const overRate = product.rates.find(r=> (r.applicationType||'').toLowerCase().includes('over'));
|
||||
return (
|
||||
<>
|
||||
{newRate && (
|
||||
<div className="text-sm text-gray-700"><strong>New Lawn:</strong> {newRate.rateAmount} {newRate.rateUnit}</div>
|
||||
)}
|
||||
{overRate && (
|
||||
<div className="text-sm text-gray-700"><strong>Overseeding:</strong> {overRate.rateAmount} {overRate.rateUnit}</div>
|
||||
)}
|
||||
{!newRate && !overRate && product.rates.slice(0,2).map((rate, index)=> (
|
||||
<div key={index} className="text-sm text-gray-700"><strong>{rate.applicationType}:</strong> {rate.rateAmount} {rate.rateUnit}</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
})()
|
||||
) : (
|
||||
product.rates.slice(0, 2).map((rate, index) => (
|
||||
<div key={index} className="text-sm text-gray-700">
|
||||
<strong>{rate.applicationType}:</strong> {rate.rateAmount} {rate.rateUnit}
|
||||
</div>
|
||||
))}
|
||||
))
|
||||
)}
|
||||
{product.rates.length > 2 && (
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
+{product.rates.length - 2} more rates
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">+{product.rates.length - 2} more rates</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -464,6 +535,8 @@ const CreateProductModal = ({ onSubmit, onCancel, sharedProducts, categories })
|
||||
|
||||
const [spreaderSettings, setSpreaderSettings] = useState([]);
|
||||
const [seedBlend, setSeedBlend] = useState([]); // [{cultivar:'', percent:0}]
|
||||
const [seedNewRate, setSeedNewRate] = useState('');
|
||||
const [seedOverRate, setSeedOverRate] = useState('');
|
||||
const [availableSpreaders, setAvailableSpreaders] = useState([]);
|
||||
const [loadingSpreaders, setLoadingSpreaders] = useState(false);
|
||||
const [newSpreaderSetting, setNewSpreaderSetting] = useState({
|
||||
@@ -549,9 +622,17 @@ const CreateProductModal = ({ onSubmit, onCancel, sharedProducts, categories })
|
||||
spreaderSettings: formData.productType === 'granular' ? spreaderSettings : []
|
||||
};
|
||||
|
||||
// If this is a seed product and user entered a blend, pack it into activeIngredients as JSON
|
||||
if (formData.productType === 'seed' && Array.isArray(seedBlend) && seedBlend.length > 0) {
|
||||
submitData.activeIngredients = JSON.stringify({ seedBlend });
|
||||
// If seed, include blend and optional seed rates in activeIngredients JSON
|
||||
if (formData.productType === 'seed') {
|
||||
const payload = { seedBlend: Array.isArray(seedBlend) ? seedBlend : [] };
|
||||
if (seedNewRate || seedOverRate) {
|
||||
payload.seedRates = {
|
||||
new: seedNewRate ? parseFloat(seedNewRate) : null,
|
||||
overseed: seedOverRate ? parseFloat(seedOverRate) : null,
|
||||
unit: formData.customRateUnit
|
||||
};
|
||||
}
|
||||
submitData.activeIngredients = JSON.stringify(payload);
|
||||
}
|
||||
|
||||
onSubmit(submitData);
|
||||
@@ -632,7 +713,19 @@ const CreateProductModal = ({ onSubmit, onCancel, sharedProducts, categories })
|
||||
</div>
|
||||
|
||||
{formData.productType === 'seed' && (
|
||||
<>
|
||||
<SeedBlendEditor value={seedBlend} onChange={setSeedBlend} />
|
||||
<div className="grid grid-cols-2 gap-3 mt-3">
|
||||
<div>
|
||||
<label className="label text-xs">New Lawn Seeding Rate</label>
|
||||
<input type="number" step="0.01" className="input" value={seedNewRate} onChange={(e)=> setSeedNewRate(e.target.value)} placeholder="e.g., 7" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="label text-xs">Overseeding Rate</label>
|
||||
<input type="number" step="0.01" className="input" value={seedOverRate} onChange={(e)=> setSeedOverRate(e.target.value)} placeholder="e.g., 3" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div>
|
||||
@@ -840,6 +933,9 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
|
||||
activeIngredients: product.activeIngredients || '',
|
||||
description: product.description || ''
|
||||
});
|
||||
const [editSeedBlend, setEditSeedBlend] = useState([]);
|
||||
const [editSeedNewRate, setEditSeedNewRate] = useState('');
|
||||
const [editSeedOverRate, setEditSeedOverRate] = useState('');
|
||||
|
||||
const [editMode, setEditMode] = useState('basic'); // 'basic' or 'advanced'
|
||||
const [editSpreaderSettings, setEditSpreaderSettings] = useState([]);
|
||||
@@ -903,6 +999,24 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
|
||||
loadSpreaderSettings();
|
||||
}, [product.id, product.productType]);
|
||||
|
||||
// Parse seed data from activeIngredients when opening
|
||||
useEffect(() => {
|
||||
if (product.productType === 'seed') {
|
||||
try {
|
||||
const ai = typeof product.activeIngredients === 'string' ? JSON.parse(product.activeIngredients) : product.activeIngredients;
|
||||
if (ai?.seedBlend) setEditSeedBlend(ai.seedBlend);
|
||||
if (ai?.seedRates) {
|
||||
setEditSeedNewRate(ai.seedRates.new || '');
|
||||
setEditSeedOverRate(ai.seedRates.overseed || '');
|
||||
if (ai.seedRates.unit && !formData.customRateUnit) {
|
||||
setFormData(prev => ({ ...prev, customRateUnit: ai.seedRates.unit }));
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -947,7 +1061,14 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
|
||||
brand: formData.brand || null,
|
||||
categoryId: formData.categoryId ? parseInt(formData.categoryId) : null,
|
||||
productType: formData.productType || null,
|
||||
activeIngredients: formData.activeIngredients || null,
|
||||
activeIngredients: formData.productType === 'seed' ? JSON.stringify({
|
||||
seedBlend: editSeedBlend,
|
||||
seedRates: (editSeedNewRate || editSeedOverRate) ? {
|
||||
new: editSeedNewRate ? parseFloat(editSeedNewRate) : null,
|
||||
overseed: editSeedOverRate ? parseFloat(editSeedOverRate) : null,
|
||||
unit: formData.customRateUnit
|
||||
} : undefined
|
||||
}) : (formData.activeIngredients || null),
|
||||
description: formData.description || null,
|
||||
isAdvancedEdit: true, // Flag to indicate this is an advanced edit
|
||||
// Include spreader settings for granular products
|
||||
@@ -1278,6 +1399,21 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{formData.productType === 'seed' ? (
|
||||
<>
|
||||
<SeedBlendEditor value={editSeedBlend} onChange={setEditSeedBlend} />
|
||||
<div className="grid grid-cols-2 gap-3 mt-3">
|
||||
<div>
|
||||
<label className="label text-xs">New Lawn Seeding Rate</label>
|
||||
<input type="number" step="0.01" className="input" value={editSeedNewRate} onChange={(e)=> setEditSeedNewRate(e.target.value)} placeholder="e.g., 7" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="label text-xs">Overseeding Rate</label>
|
||||
<input type="number" step="0.01" className="input" value={editSeedOverRate} onChange={(e)=> setEditSeedOverRate(e.target.value)} placeholder="e.g., 3" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<label className="label">Active Ingredients</label>
|
||||
<input
|
||||
@@ -1288,6 +1424,7 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
|
||||
placeholder="e.g., 2,4-D 38.3%, Dicamba 2.77%"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<label className="label">Description</label>
|
||||
|
||||
Reference in New Issue
Block a user