update seed blend
This commit is contained in:
@@ -1,18 +1,11 @@
|
||||
node_modules
|
||||
build
|
||||
dist
|
||||
.cache
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
.env
|
||||
.nyc_output
|
||||
coverage
|
||||
.coverage
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
*.log
|
||||
coverage
|
||||
tmp
|
||||
.env
|
||||
*.local
|
||||
|
||||
@@ -12,11 +12,6 @@ RUN npm install --only=production --silent
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Use the pre-created non-root user from the Node image
|
||||
# and ensure ownership is correct
|
||||
RUN chown -R node:node /app
|
||||
USER node
|
||||
|
||||
# Expose port
|
||||
EXPOSE 5000
|
||||
|
||||
|
||||
@@ -389,6 +389,7 @@ router.get('/products', async (req, res, next) => {
|
||||
categoryName: product.category_name,
|
||||
productType: product.product_type,
|
||||
activeIngredients: product.active_ingredients,
|
||||
seedBlend: product.seed_blend,
|
||||
description: product.description,
|
||||
rateCount: parseInt(product.rate_count),
|
||||
usageCount: parseInt(product.usage_count),
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
node_modules
|
||||
build
|
||||
dist
|
||||
.cache
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
.env
|
||||
.nyc_output
|
||||
coverage
|
||||
.coverage
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
*.log
|
||||
coverage
|
||||
tmp
|
||||
.env
|
||||
*.local
|
||||
|
||||
@@ -3,20 +3,13 @@ FROM node:18-alpine
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
# Copy package files and install dependencies
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install --silent
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Use the pre-created non-root user from the Node image
|
||||
# and ensure ownership is correct
|
||||
RUN chown -R node:node /app
|
||||
USER node
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ const AdminProducts = () => {
|
||||
productType: 'granular',
|
||||
activeIngredients: '',
|
||||
description: '',
|
||||
seedBlend: [],
|
||||
rates: [{ applicationType: 'granular', rateAmount: '', rateUnit: 'lbs/1000 sq ft', notes: '' }]
|
||||
});
|
||||
|
||||
@@ -201,6 +202,7 @@ const AdminProducts = () => {
|
||||
productType: 'granular',
|
||||
activeIngredients: '',
|
||||
description: '',
|
||||
seedBlend: [],
|
||||
rates: [{ applicationType: 'granular', rateAmount: '', rateUnit: 'lbs/1000 sq ft', notes: '' }]
|
||||
});
|
||||
setSelectedProduct(null);
|
||||
@@ -215,6 +217,7 @@ const AdminProducts = () => {
|
||||
productType: product.productType || product.customProductType || 'granular',
|
||||
activeIngredients: product.activeIngredients || product.customActiveIngredients || '',
|
||||
description: product.description || product.customDescription || '',
|
||||
seedBlend: product.seedBlend || [],
|
||||
rates: product.rates && product.rates.length > 0 ? product.rates : [{
|
||||
applicationType: product.productType || 'granular',
|
||||
rateAmount: product.customRateAmount || '',
|
||||
@@ -340,6 +343,67 @@ const AdminProducts = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Seed blend editor */}
|
||||
{formData.productType === 'seed' && (
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<label className="block text-sm font-medium text-gray-700">Seed Blend (Cultivars + %)</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setFormData({ ...formData, seedBlend: [...(formData.seedBlend||[]), { cultivar: '', percent: '' }] })}
|
||||
className="text-sm text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
+ Add Cultivar
|
||||
</button>
|
||||
</div>
|
||||
{(formData.seedBlend && formData.seedBlend.length > 0) ? (
|
||||
<div className="space-y-2">
|
||||
{formData.seedBlend.map((row, idx) => (
|
||||
<div key={idx} className="grid grid-cols-6 gap-2 items-center">
|
||||
<input
|
||||
className="col-span-4 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="Cultivar name"
|
||||
value={row.cultivar}
|
||||
onChange={(e)=> {
|
||||
const next = [...formData.seedBlend];
|
||||
next[idx] = { ...next[idx], cultivar: e.target.value };
|
||||
setFormData({ ...formData, seedBlend: next });
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
step="0.1"
|
||||
className="col-span-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="%"
|
||||
value={row.percent}
|
||||
onChange={(e)=> {
|
||||
const next = [...formData.seedBlend];
|
||||
next[idx] = { ...next[idx], percent: e.target.value };
|
||||
setFormData({ ...formData, seedBlend: next });
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="text-red-600"
|
||||
onClick={()=> {
|
||||
const next = (formData.seedBlend||[]).filter((_,i)=> i!==idx);
|
||||
setFormData({ ...formData, seedBlend: next });
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<div className="text-xs text-gray-600">
|
||||
Total: {((formData.seedBlend||[]).reduce((s,r)=> s + (parseFloat(r.percent)||0), 0)).toFixed(1)}%
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-sm text-gray-500">No cultivars added yet.</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Application Rates */}
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
@@ -912,4 +976,4 @@ const AdminProducts = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminProducts;
|
||||
export default AdminProducts;
|
||||
|
||||
@@ -1471,7 +1471,7 @@ const EditProductModal = ({ product, onSubmit, onCancel, sharedProducts, categor
|
||||
};
|
||||
|
||||
// Editor component for seed blends
|
||||
const SeedBlendEditor = ({ value = [], onChange }) => {
|
||||
function SeedBlendEditor({ value = [], onChange }) {
|
||||
const [rows, setRows] = React.useState(value || []);
|
||||
React.useEffect(()=>{ onChange && onChange(rows); }, [rows]);
|
||||
const addRow = () => setRows([...(rows||[]), { cultivar: '', percent: '' }]);
|
||||
@@ -1502,6 +1502,6 @@ const SeedBlendEditor = ({ value = [], onChange }) => {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default Products;
|
||||
|
||||
Reference in New Issue
Block a user