diff --git a/README.md b/README.md
index 1ef914b1..dc82fe74 100644
--- a/README.md
+++ b/README.md
@@ -133,6 +133,7 @@ Note: Backend and frontend must be running simultaneously for proper functionali
## 🌟 Core Features
- 🌾 Crop Recommendation
+- 🌿 Fertilizer Recommendation
- 📉 Yield Prediction
- 🔬 Disease Detection
- � **AI Chatbot** - Platform guidance & agriculture support
@@ -226,6 +227,7 @@ AGRITECH/
│ ├── 📁 css/ # Stylesheets
│ └── 📁 js/ # Client-side scripts
├── 📁 Crop Recommendation/ # 🌾 Crop recommendation module
+├── 📁 Fertiliser Recommendation System/ # 🌿 Fertilizer recommendation ML module
├── 📁 Disease Prediction/ # 🔬 Disease detection module
├── 📁 Crop Yield Prediction/ # 📊 Yield forecasting module
├── 📁 Community/ # 💬 community/forum backend
diff --git a/disease.js b/disease.js
index 309a2012..8d37f97c 100644
--- a/disease.js
+++ b/disease.js
@@ -255,6 +255,8 @@ const loading = document.getElementById("loading");
const resultsDiv = document.getElementById("results");
let selectedFile = null;
+let uploadInProgress = false;
+let fileSelectionToken = 0;
// File upload handling
uploadArea.addEventListener("click", () => fileInput.click());
@@ -308,7 +310,7 @@ fileInput.addEventListener("change", (e) => {
// Analysis function
analyzeBtn.addEventListener("click", async () => {
- if (!selectedFile) return;
+ if (uploadInProgress || !selectedFile) return;
loading.style.display = "block";
resultsDiv.innerHTML = "";
@@ -470,40 +472,59 @@ function validateImage(file) {
return true;
}
+function readPreviewDataUrl(file) {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+
+ reader.onload = (event) => resolve(event.target.result);
+ reader.onerror = () => reject(new Error("Error loading image preview."));
+ reader.readAsDataURL(file);
+ });
+}
+
// Enhanced file handling with validation
-function handleFileSelect(file) {
+async function handleFileSelect(file) {
+ const currentSelectionToken = ++fileSelectionToken;
+
try {
validateImage(file);
- selectedFile = file;
+ uploadInProgress = true;
+ selectedFile = null;
+ analyzeBtn.disabled = true;
previewContainer.innerHTML = '
';
- const reader = new FileReader();
- reader.onload = (e) => {
- previewContainer.innerHTML = `
`;
- analyzeBtn.disabled = false;
- resultsDiv.innerHTML = "";
- document.getElementById("downloadPdfBtn").style.display = "none";
+ const previewDataUrl = await readPreviewDataUrl(file);
- document.getElementById("modelStatus").innerHTML =
- "🔍 Image loaded - Ready for analysis";
- document.getElementById("modelStatus").className =
- "status-indicator status-warning";
- };
+ if (currentSelectionToken !== fileSelectionToken) {
+ return;
+ }
+
+ selectedFile = file;
+ previewContainer.innerHTML = `
`;
+ resultsDiv.innerHTML = "";
+ document.getElementById("downloadPdfBtn").style.display = "none";
- reader.onerror = () => {
- previewContainer.innerHTML =
- '❌ Error loading image
';
- selectedFile = null;
- };
+ document.getElementById("modelStatus").innerHTML =
+ "🔍 Image loaded - Ready for analysis";
+ document.getElementById("modelStatus").className =
+ "status-indicator status-warning";
- reader.readAsDataURL(file);
+ analyzeBtn.disabled = false;
} catch (error) {
+ if (currentSelectionToken !== fileSelectionToken) {
+ return;
+ }
+
alert(error.message);
previewContainer.innerHTML =
'📷 Upload an image to get started
';
selectedFile = null;
analyzeBtn.disabled = true;
+ } finally {
+ if (currentSelectionToken === fileSelectionToken) {
+ uploadInProgress = false;
+ }
}
}
diff --git a/domain/README.md b/domain/README.md
index 3f06e4d9..e5545645 100644
--- a/domain/README.md
+++ b/domain/README.md
@@ -9,6 +9,8 @@ This directory defines shared domain contracts used across AgriTech.
## Current Schemas
- Crop
+- FertilizerRecommendationInput
+- FertilizerRecommendation
- GrowthStage
- SoilData
- PredictionResult
diff --git a/domain/fertilizer.schema.js b/domain/fertilizer.schema.js
new file mode 100644
index 00000000..577660f7
--- /dev/null
+++ b/domain/fertilizer.schema.js
@@ -0,0 +1,79 @@
+/**
+ * Fertilizer recommendation schema
+ */
+
+export const FertilizerRecommendationInputSchema = {
+ $id: "FertilizerRecommendationInput",
+ type: "object",
+ required: [
+ "temperature",
+ "humidity",
+ "moisture",
+ "soilType",
+ "cropType",
+ "nitrogen",
+ "phosphorous",
+ "potassium"
+ ],
+ additionalProperties: false,
+ properties: {
+ temperature: {
+ type: "number",
+ description: "Ambient temperature in degrees Celsius"
+ },
+ humidity: {
+ type: "number",
+ description: "Relative humidity percentage"
+ },
+ moisture: {
+ type: "number",
+ description: "Soil moisture percentage"
+ },
+ soilType: {
+ type: "string",
+ description: "Categorical soil type"
+ },
+ cropType: {
+ type: "string",
+ description: "Categorical crop type"
+ },
+ nitrogen: {
+ type: "number",
+ description: "Nitrogen content"
+ },
+ phosphorous: {
+ type: "number",
+ description: "Phosphorous content"
+ },
+ potassium: {
+ type: "number",
+ description: "Potassium content"
+ }
+ }
+};
+
+export const FertilizerRecommendationSchema = {
+ $id: "FertilizerRecommendation",
+ type: "object",
+ required: ["fertilizerName", "confidence", "inputs"],
+ additionalProperties: false,
+ properties: {
+ fertilizerName: {
+ type: "string",
+ description: "Recommended fertilizer label"
+ },
+ confidence: {
+ type: "number",
+ description: "Prediction confidence score (0-1)"
+ },
+ inputs: FertilizerRecommendationInputSchema,
+ modelVersion: {
+ type: "string",
+ description: "Version of the fertilizer recommendation model"
+ },
+ createdAt: {
+ type: "string",
+ description: "ISO timestamp when the recommendation was generated"
+ }
+ }
+};
\ No newline at end of file
diff --git a/domain/index.js b/domain/index.js
index 12eae6bd..e3a696ae 100644
--- a/domain/index.js
+++ b/domain/index.js
@@ -1,10 +1,13 @@
import { CropSchema } from "./crop.schema.js";
+import { FertilizerRecommendationInputSchema, FertilizerRecommendationSchema } from "./fertilizer.schema.js";
import { GrowthStageSchema } from "./growthStage.schema.js";
import { PredictionResultSchema } from "./prediction.schema.js";
import { SoilDataSchema } from "./soil.schema.js";
export {
CropSchema,
+ FertilizerRecommendationInputSchema,
+ FertilizerRecommendationSchema,
GrowthStageSchema,
PredictionResultSchema,
SoilDataSchema
@@ -12,6 +15,8 @@ export {
export const DomainSchemas = {
Crop: CropSchema,
+ FertilizerRecommendationInput: FertilizerRecommendationInputSchema,
+ FertilizerRecommendation: FertilizerRecommendationSchema,
GrowthStage: GrowthStageSchema,
PredictionResult: PredictionResultSchema,
SoilData: SoilDataSchema
diff --git a/domain/validate.js b/domain/validate.js
index 2e9e394e..110e47d6 100644
--- a/domain/validate.js
+++ b/domain/validate.js
@@ -1,5 +1,7 @@
import {
CropSchema,
+ FertilizerRecommendationInputSchema,
+ FertilizerRecommendationSchema,
GrowthStageSchema,
PredictionResultSchema,
SoilDataSchema
@@ -7,6 +9,8 @@ import {
const schemaMap = {
Crop: CropSchema,
+ FertilizerRecommendationInput: FertilizerRecommendationInputSchema,
+ FertilizerRecommendation: FertilizerRecommendationSchema,
GrowthStage: GrowthStageSchema,
PredictionResult: PredictionResultSchema,
SoilData: SoilDataSchema