diff --git a/backend/.dockerignore b/backend/.dockerignore index 745fa62..474a044 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -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 -*~ \ No newline at end of file +*.log +coverage +tmp +.env +*.local diff --git a/backend/Dockerfile b/backend/Dockerfile index 796668e..9edf68a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -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 diff --git a/backend/src/routes/admin.js b/backend/src/routes/admin.js index 90b4d58..388c843 100644 --- a/backend/src/routes/admin.js +++ b/backend/src/routes/admin.js @@ -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), diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 745fa62..474a044 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -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 -*~ \ No newline at end of file +*.log +coverage +tmp +.env +*.local diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 97573bf..f93f7a8 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -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 diff --git a/frontend/src/pages/Admin/AdminProducts.js b/frontend/src/pages/Admin/AdminProducts.js index c899070..58158e6 100644 --- a/frontend/src/pages/Admin/AdminProducts.js +++ b/frontend/src/pages/Admin/AdminProducts.js @@ -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 = () => { /> + {/* Seed blend editor */} + {formData.productType === 'seed' && ( +
+
+ + +
+ {(formData.seedBlend && formData.seedBlend.length > 0) ? ( +
+ {formData.seedBlend.map((row, idx) => ( +
+ { + const next = [...formData.seedBlend]; + next[idx] = { ...next[idx], cultivar: e.target.value }; + setFormData({ ...formData, seedBlend: next }); + }} + /> + { + const next = [...formData.seedBlend]; + next[idx] = { ...next[idx], percent: e.target.value }; + setFormData({ ...formData, seedBlend: next }); + }} + /> + +
+ ))} +
+ Total: {((formData.seedBlend||[]).reduce((s,r)=> s + (parseFloat(r.percent)||0), 0)).toFixed(1)}% +
+
+ ) : ( +
No cultivars added yet.
+ )} +
+ )} + {/* Application Rates */}
@@ -912,4 +976,4 @@ const AdminProducts = () => { ); }; -export default AdminProducts; \ No newline at end of file +export default AdminProducts; diff --git a/frontend/src/pages/Products/Products.js b/frontend/src/pages/Products/Products.js index a072b97..409afe9 100644 --- a/frontend/src/pages/Products/Products.js +++ b/frontend/src/pages/Products/Products.js @@ -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 }) => { )}
); -}; +} export default Products;