';
}
}
// Render forecast sidebar from a weather_data object (returned by chat endpoint)
function renderForecastFromData(weatherData) {
if (!weatherData || !weatherData.forecast || !forecastCards) return;
if (weatherData.forecast.length > 0) {
const first = weatherData.forecast[0];
updateWeatherSummaryInAccordion(first.condition, Math.round(first.temp_max), '');
}
let cardsHtml = '';
for (const day of weatherData.forecast) {
const icon = getWeatherIcon(day.condition);
cardsHtml += `
${day.day.substring(0, 3)}
${icon}
${Math.round(day.temp_max)}°
${day.rain_chance}%
`;
}
forecastCards.innerHTML = cardsHtml;
if (weatherWidget) weatherWidget.style.display = 'block';
}
function updateSensorSummaryInAccordion(moisture, temp) {
const sensorHeaderSpan = document.querySelector('.accordion-header[data-accordion="sensorData"] .accordion-title');
if (sensorHeaderSpan) {
sensorHeaderSpan.innerHTML = `📊 Sensor Data (Soil: ${moisture || '--'}%, ${temp || '--'}°C)`;
}
}
function updateCropSummaryInAccordion(crop, bbch) {
const cropHeaderSpan = document.querySelector('.accordion-header[data-accordion="cropInfo"] .accordion-title');
if (cropHeaderSpan) {
const cropDisplay = crop || 'Not set';
const bbchDisplay = bbch || '--';
cropHeaderSpan.innerHTML = `🌾 Crop Info (${cropDisplay}, BBCH ${bbchDisplay})`;
}
}
// Monitor crop type and BBCH changes — mark dirty so next message includes updated values
if (cropType) {
cropType.addEventListener('change', function() {
cropDirty = true;
bbchDirty = true; // reset BBCH too when crop changes
updateCropSummaryInAccordion(cropType.value, bbchStage.value);
});
}
if (bbchStage) {
bbchStage.addEventListener('input', function() {
bbchDirty = true;
updateCropSummaryInAccordion(cropType.value, bbchStage.value);
});
}
// Monitor sensor inputs
if (soilMoisture) {
soilMoisture.addEventListener('input', function() {
updateSensorSummaryInAccordion(soilMoisture.value, soilTemp.value);
});
}
if (soilTemp) {
soilTemp.addEventListener('input', function() {
updateSensorSummaryInAccordion(soilMoisture.value, soilTemp.value);
});
}
// Get Forecast button handler (city mode)
if (getForecastBtn) {
getForecastBtn.addEventListener('click', function() {
const location = locationInput.value.trim();
if (!location) {
alert('Please enter a location first');
return;
}
updateForecastWidget(location);
});
}
function isWeatherQuestion(message) {
const weatherKeywords = ['weather', 'forecast', 'rain', 'temperature', 'temp', 'sunny', 'cloudy', 'storm', 'humid'];
const msgLower = message.toLowerCase();
return weatherKeywords.some(keyword => msgLower.includes(keyword));
}
// Handle image attachment with thumbnail preview
const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
const MAX_IMAGE_BYTES = 5 * 1024 * 1024; // 5 MB
if (imageInput) {
imageInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
if (!ALLOWED_IMAGE_TYPES.includes(file.type)) {
alert('Only image files are supported (JPEG, PNG, WebP, GIF).');
imageInput.value = '';
return;
}
if (file.size > MAX_IMAGE_BYTES) {
alert('Image is too large. Please use an image under 5 MB.');
imageInput.value = '';
return;
}
const reader = new FileReader();
reader.onloadend = function() {
attachedImageBase64 = reader.result;
if (thumbImg) thumbImg.src = attachedImageBase64;
if (imagePreviewThumb) imagePreviewThumb.style.display = 'flex';
};
reader.readAsDataURL(file);
});
}
// Remove attached image thumbnail
if (removeThumbBtn) {
removeThumbBtn.addEventListener('click', function() {
attachedImageBase64 = null;
if (imageInput) imageInput.value = '';
if (imagePreviewThumb) imagePreviewThumb.style.display = 'none';
if (thumbImg) thumbImg.src = '';
});
}
// Send chat message
async function sendMessage() {
const message = questionInput.value.trim();
if (!message && !attachedImageBase64) return;
console.log("=== SEND MESSAGE DEBUG ===");
console.log("Message:", message);
console.log("attachedImageBase64 exists:", attachedImageBase64 ? "YES" : "NO");
if (attachedImageBase64) {
console.log("Image length:", attachedImageBase64.length);
}
// Store image before clearing
const imageToSend = attachedImageBase64;
// Show user message with image (if any)
if (imageToSend) {
addUserMessageWithImage(message, imageToSend);
} else {
addMessageStatic(message, true);
}
questionInput.value = '';
showLoading();
// Crop and BBCH are ALWAYS enabled - no toggle checks needed
const sensorsEnabled = sensorsToggle ? sensorsToggle.checked : true;
const weatherEnabled = weatherToggle ? weatherToggle.checked : true;
const requestBody = {
message: message,
session_id: sessionId
};
// Add image if we have one
if (imageToSend) {
requestBody.image_base64 = imageToSend;
console.log("Added image to requestBody, length:", imageToSend.length);
}
// Send crop/BBCH only on first message or when user explicitly changed them
if (cropDirty && cropType.value) {
requestBody.crop_type = cropType.value;
cropDirty = false;
}
// Always send farming_type if a value is selected — backend handles smart injection
if (farmingType && farmingType.value) {
requestBody.farming_type = farmingType.value;
}
if (bbchDirty && bbchStage.value) {
requestBody.bbch_stage = bbchStage.value;
bbchDirty = false;
}
if (locationInput.value) {
requestBody.location = locationInput.value;
}
if (sensorsEnabled) {
if (soilMoisture.value) requestBody.soil_moisture = parseFloat(soilMoisture.value);
if (soilTemp.value) requestBody.soil_temperature = parseFloat(soilTemp.value);
if (airTemp.value) requestBody.air_temperature = parseFloat(airTemp.value);
if (humidity.value) requestBody.humidity = parseFloat(humidity.value);
}
requestBody.sensors_enabled = sensorsEnabled;
requestBody.weather_enabled = weatherEnabled;
requestBody.crop_detection_enabled = true; // ALWAYS enabled
requestBody.bbch_detection_enabled = true; // ALWAYS enabled
// Clear the attached image AFTER building request body
attachedImageBase64 = null;
if (imageInput) imageInput.value = '';
if (imagePreviewThumb) imagePreviewThumb.style.display = 'none';
if (thumbImg) thumbImg.src = '';
console.log("Request body keys:", Object.keys(requestBody));
console.log("Sending request...");
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
});
console.log("Response status:", response.status);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Chat failed');
}
const data = await response.json();
hideLoading();
await addMessageWithTyping(data.response, false);
if (data.context_used && data.context_used.includes('crop_context')) {
updateCropSummaryInAccordion(cropType.value, bbchStage.value);
}
// Use weather data returned by the chat endpoint (single source of truth)
if (data.weather_data && data.weather_data.forecast) {
renderForecastFromData(data.weather_data);
}
} catch (error) {
hideLoading();
const userMsg = error.message && error.message !== 'Failed to fetch'
? error.message
: 'Connection error. Please check your network and try again.';
addMessageStatic('Sorry, something went wrong: ' + userMsg, false);
console.error(error);
}
}
function clearChat() {
// Clear old session memory on backend before generating new session
fetch('/api/clear-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_id: sessionId }),
}).catch(() => {}); // fire-and-forget, don't block UI
chatBox.innerHTML = '
👋 Hello! I\'m AllTech AI. Ask me about crop management, fertilizers, irrigation, or diseases.