customized_farm_planner / templates /farmer_dashboard.html
pranit144's picture
Upload 56 files
429a26d verified
{% extends "base.html" %}
{% block title %}Farmer Dashboard - Farm Management Portal{% endblock %}
{% block content %}
<div class="container mt-4">
<!-- Welcome Header -->
<div class="row mb-4">
<div class="col-12">
<div class="card bg-success text-white">
<div class="card-body">
<h2><i class="fas fa-tachometer-alt me-2"></i>Welcome, {{ farmer.name }}!</h2>
<p class="mb-0">Manage your farms and get AI-powered daily recommendations</p>
</div>
</div>
</div>
</div>
<!-- External AI Tools Quick Access -->
<!-- Quick Stats -->
<div class="row mb-4">
<div class="col-md-3 mb-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-seedling fa-2x text-success mb-2"></i>
<h5>{{ farms|length }}</h5>
<small class="text-muted">Total Farms</small>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-calendar-check fa-2x text-primary mb-2"></i>
<h5>{{ recent_activities|length }}</h5>
<small class="text-muted">Recent Activities</small>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-sms fa-2x text-info mb-2"></i>
<h5>{% if today_advisory %}Active{% else %}Pending{% endif %}</h5>
<small class="text-muted">Today's Advisory</small>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-user fa-2x text-warning mb-2"></i>
<h5>{{ farmer.contact_number }}</h5>
<small class="text-muted">Contact</small>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Today's Advisory -->
<div class="col-md-8 mb-4">
<div class="card">
<div class="card-header bg-primary text-white">
<h5><i class="fas fa-brain me-2"></i>Today's AI Advisory</h5>
</div>
<div class="card-body">
{% if today_advisory %}
<div class="alert alert-success">
<h6><i class="fas fa-check-circle me-2"></i>Tasks to Do:</h6>
<p>{{ today_advisory.task_to_do }}</p>
</div>
<div class="alert alert-warning">
<h6><i class="fas fa-exclamation-triangle me-2"></i>Tasks to Avoid:</h6>
<p>{{ today_advisory.task_to_avoid }}</p>
</div>
{% if today_advisory.reason_explanation %}
<div class="alert alert-info">
<h6><i class="fas fa-info-circle me-2"></i>Explanation:</h6>
<p>{{ today_advisory.reason_explanation }}</p>
</div>
{% endif %}
{% if farms %}
<div class="mt-3">
<button class="btn btn-success" onclick="sendSMSAdvisory({{ farms[0].id }})">
<i class="fas fa-sms me-2"></i>Send SMS Alert
</button>
<button class="btn btn-info" onclick="sendTelegramAdvisory({{ farms[0].id }})">
<i class="fab fa-telegram me-2"></i>Send Telegram Alert
</button>
<button class="btn btn-primary" onclick="generateNewAdvisory({{ farms[0].id }})">
<i class="fas fa-sync me-2"></i>Refresh Advisory
</button>
</div>
{% endif %}
{% else %}
<div class="text-center text-muted py-4">
<i class="fas fa-robot fa-3x mb-3"></i>
<h6>No advisory generated yet for today</h6>
<p>Click below to generate AI-powered recommendations</p>
{% if farms %}
<button class="btn btn-primary" onclick="generateNewAdvisory({{ farms[0].id }})">
<i class="fas fa-magic me-2"></i>Generate Advisory
</button>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="col-md-4 mb-4">
<div class="card">
<div class="card-header bg-secondary text-white">
<h5><i class="fas fa-bolt me-2"></i>Quick Actions</h5>
<small><i class="fas fa-robot me-1"></i>AI-powered tools for better farming</small>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="{{ url_for('add_farm') }}" class="btn btn-success">
<i class="fas fa-plus me-2"></i>Add New Farm
</a>
{% if farms %}
<a href="{{ url_for('farm_details', farm_id=farms[0].id) }}" class="btn btn-primary">
<i class="fas fa-eye me-2"></i>View Farm Details
</a>
<button class="btn btn-success" onclick="window.open('https://pranit144-weather-forecast-farmers.hf.space', '_blank')" title="AI-powered weather forecasting tool">
<i class="fas fa-cloud me-2"></i>Smart Weather Forecast
</button>
<button class="btn btn-warning" onclick="viewWeatherAlerts({{ farms[0].id }})">
<i class="fas fa-exclamation-triangle me-2"></i>Weather Alerts
</button>
<button class="btn btn-info" onclick="window.open('https://agri-ai-rosy.vercel.app/cropMarketTrendAnalyzer', '_blank')" title="AI market trend analyzer">
<i class="fas fa-chart-line me-2"></i>Market Analyzer
</button>
<button class="btn btn-danger" onclick="window.open('https://agri-ai-rosy.vercel.app/plant-disease-detector', '_blank')" title="AI plant disease detector">
<i class="fas fa-search me-2"></i>Disease Detector
</button>
<button class="btn btn-secondary" onclick="showSendImageModal()">
<i class="fas fa-paper-plane me-2"></i>Send Image via Telegram
</button>
{% endif %}
<a href="{{ url_for('farmer_logout') }}" class="btn btn-outline-danger">
<i class="fas fa-sign-out-alt me-2"></i>Logout
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Daily Tasks Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-info text-white d-flex justify-content-between align-items-center">
<h5><i class="fas fa-tasks me-2"></i>Today's Daily Tasks</h5>
<small id="task-date">{{ today_date or 'Today' }}</small>
</div>
<div class="card-body">
<div id="daily-tasks-container">
<div class="text-center text-muted py-4" id="no-tasks-message">
<i class="fas fa-clipboard-list fa-3x mb-3"></i>
<h6>No daily tasks loaded</h6>
<p>Generate daily tasks to get AI-powered farming recommendations</p>
</div>
</div>
<div class="mt-3 text-center">
<div class="btn-group" role="group">
<button class="btn btn-primary" onclick="loadDailyTasks()">
<i class="fas fa-sync me-2"></i>Load Today's Tasks
</button>
<button class="btn btn-success" onclick="generateDailyTasks()">
<i class="fas fa-magic me-2"></i>Generate New Tasks
</button>
<button class="btn btn-info" onclick="sendTasksTelegram()" title="Send tasks to your Telegram">
<i class="fab fa-telegram me-2"></i>Send to Telegram
</button>
<button class="btn btn-outline-danger" onclick="deleteAllTasks()" title="Delete all tasks for today">
<i class="fas fa-trash me-2"></i>Clear All
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Farms List -->
{% if farms %}
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-list me-2"></i>Your Farms</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Farm Name</th>
<th>Size (Acres)</th>
<th>Crops</th>
<th>Irrigation</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for farm in farms %}
<tr>
<td>{{ farm.farm_name }}</td>
<td>{{ farm.farm_size }}</td>
<td>
{% for crop in farm.get_crop_types() %}
<span class="badge bg-success me-1">{{ crop }}</span>
{% endfor %}
</td>
<td>{{ farm.irrigation_type }}</td>
<td>
<div class="btn-group" role="group">
<!-- Primary Actions -->
<a href="{{ url_for('farm_details', farm_id=farm.id) }}" class="btn btn-sm btn-outline-primary" title="View Details">
<i class="fas fa-eye"></i>
</a>
<button class="btn btn-sm btn-outline-success" onclick="generateNewAdvisory({{ farm.id }})" title="Generate Daily Advisory">
<i class="fas fa-brain"></i>
</button>
</div>
<!-- Yearly Plan Actions -->
<div class="btn-group ms-1" role="group">
<button class="btn btn-sm btn-outline-info" onclick="generateYearlyPlan({{ farm.id }})" title="Generate Yearly Plan">
<i class="fas fa-calendar-plus"></i>
</button>
<button class="btn btn-sm btn-outline-secondary" onclick="viewYearlyPlan({{ farm.id }})" title="View Yearly Plan">
<i class="fas fa-calendar-alt"></i>
</button>
<button class="btn btn-sm btn-outline-warning" onclick="editYearlyPlan({{ farm.id }})" title="Edit Yearly Plan">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline-dark" onclick="downloadYearlyPlanPDF({{ farm.id }})" title="Download Yearly Plan PDF">
<i class="fas fa-file-pdf"></i>
</button>
<button class="btn btn-sm btn-outline-primary" onclick="sendYearlyPlanTelegram({{ farm.id }})" title="Send Yearly Plan via Telegram">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<!-- Smart Features -->
<div class="btn-group ms-1" role="group">
<button class="btn btn-sm btn-outline-info" onclick="window.open('https://pranit144-weather-forecast-farmers.hf.space', '_blank')" title="Weather Forecast">
<i class="fas fa-cloud"></i>
</button>
<button class="btn btn-sm btn-outline-success" onclick="window.open('https://agri-ai-rosy.vercel.app/cropMarketTrendAnalyzer', '_blank')" title="Market Prices">
<i class="fas fa-chart-line"></i>
</button>
<button class="btn btn-sm btn-outline-danger" onclick="window.open('https://agri-ai-rosy.vercel.app/plant-disease-detector', '_blank')" title="Disease Detection">
<i class="fas fa-bug"></i>
</button>
</div>
<!-- Delete Actions -->
<div class="btn-group ms-1" role="group">
<button class="btn btn-sm btn-outline-danger" onclick="deleteYearlyPlan({{ farm.id }})" title="Delete Yearly Plan">
<i class="fas fa-calendar-times"></i>
</button>
<button class="btn btn-sm btn-danger" onclick="deleteFarm({{ farm.id }})" title="Delete Farm"
style="opacity: 0.7;" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.7'">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endif %}
<!-- Recent Activities -->
{% if recent_activities %}
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-history me-2"></i>Recent Activities</h5>
</div>
<div class="card-body">
<div class="timeline">
{% for activity in recent_activities %}
<div class="timeline-item mb-3">
<div class="d-flex">
<div class="flex-shrink-0">
<i class="fas fa-circle text-success"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-1">{{ activity.activity_type|title }}</h6>
<p class="mb-1">{{ activity.activity_description }}</p>
<small class="text-muted">
Scheduled: {{ activity.scheduled_date.strftime('%d %b %Y') }}
| Status: <span class="badge bg-{{ 'success' if activity.status == 'completed' else 'warning' }}">{{ activity.status|title }}</span>
</small>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<!-- Loading Modal -->
<div class="modal fade" id="loadingModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3 mb-0">Processing your request...</p>
</div>
</div>
</div>
</div>
<!-- Yearly Plan Modal -->
<div class="modal fade" id="yearlyPlanModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Yearly Plan</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="yearlyPlanContent">Loading...</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="saveYearlyPlanBtn" style="display:none;">Save</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Helper to show/hide the Bootstrap 5 modal using vanilla JS
function _getLoadingModalInstance(){
const modalEl = document.getElementById('loadingModal');
return bootstrap.Modal.getOrCreateInstance(modalEl);
}
function _showYearlyPlanModal(html, isEdit = false) {
const el = document.getElementById('yearlyPlanContent');
el.innerHTML = html;
const saveBtn = document.getElementById('saveYearlyPlanBtn');
if (isEdit) {
saveBtn.style.display = 'inline-block';
} else {
saveBtn.style.display = 'none';
}
const m = new bootstrap.Modal(document.getElementById('yearlyPlanModal'));
m.show();
}
function generateYearlyPlan(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/farm/${farmId}/yearly_plan/generate`, {method: 'POST'})
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
alert('Yearly plan generated successfully!');
location.reload();
} else {
alert('Failed to generate plan: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error: ' + error.message);
});
}
function viewYearlyPlan(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/farm/${farmId}/yearly_plan`)
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success && data.plan) {
if (data.is_html && data.html_content) {
// Open comprehensive HTML plan in a new window for better viewing
const newWindow = window.open('', '_blank', 'width=1200,height=800,scrollbars=yes,resizable=yes');
newWindow.document.write(data.html_content);
newWindow.document.close();
newWindow.document.title = `Yearly Plan - ${data.plan.farm_name}`;
} else {
// Fallback to modal for simple plans
const html = `
<div class="card">
<div class="card-header bg-success text-white">
<h6><i class="fas fa-seedling me-2"></i>${data.plan.farm_name || 'Farm'}</h6>
</div>
<div class="card-body">
<div class="mb-2">
<span class="badge bg-info">Year: ${data.plan.year || new Date().getFullYear()}</span>
${data.plan.ai_generated ? '<span class="badge bg-success ms-2">AI Generated</span>' : '<span class="badge bg-secondary ms-2">Basic Plan</span>'}
</div>
<h6>Summary:</h6>
<p class="text-muted">${data.plan.summary_text || 'No summary available'}</p>
<h6>Plan Details:</h6>
<pre class="bg-light p-3 rounded" style="max-height: 400px; overflow-y: auto;">${JSON.stringify(data.plan.plan_json || data.plan.plan, null, 2)}</pre>
<small class="text-muted">Generated: ${new Date(data.plan.generated_at || data.plan.created_at).toLocaleDateString()}</small>
</div>
<div class="card-footer text-center">
<button class="btn btn-primary btn-sm" onclick="generateYearlyPlan(${farmId})">
<i class="fas fa-sync-alt me-1"></i>Regenerate with AI
</button>
</div>
</div>
`;
_showYearlyPlanModal(html);
}
} else {
alert('No yearly plan found for this farm. Generate one first.');
}
})
.catch(error => {
modal.hide();
alert('Error loading plan: ' + error.message);
});
}
function editYearlyPlan(farmId) {
// First load the existing plan
fetch(`/farmer/farm/${farmId}/yearly_plan`)
.then(response => response.json())
.then(data => {
if (!data.success || !data.plan) {
alert('No plan to edit. Generate one first.');
return;
}
const planData = data.plan;
const html = `
<div class="mb-3">
<label class="form-label"><strong>Edit Plan for ${planData.farm_name}</strong></label>
</div>
<div class="mb-3">
<label for="planSummary" class="form-label">Summary</label>
<textarea id="planSummary" class="form-control" rows="3">${planData.summary_text || ''}</textarea>
</div>
<div class="mb-3">
<label for="planJson" class="form-label">Plan Details (JSON)</label>
<textarea id="planJson" class="form-control" rows="10">${JSON.stringify(planData.plan_json, null, 2)}</textarea>
</div>
`;
_showYearlyPlanModal(html, true);
// Set up save button handler
document.getElementById('saveYearlyPlanBtn').onclick = function() {
const summary = document.getElementById('planSummary').value;
const jsonText = document.getElementById('planJson').value;
let planJson;
try {
planJson = JSON.parse(jsonText);
} catch (err) {
alert('Invalid JSON format: ' + err.message);
return;
}
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/farm/${farmId}/yearly_plan`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
summary_text: summary,
plan_json: planJson
})
})
.then(response => response.json())
.then(result => {
modal.hide();
if (result.success) {
alert('Plan updated successfully!');
bootstrap.Modal.getInstance(document.getElementById('yearlyPlanModal')).hide();
location.reload();
} else {
alert('Failed to save: ' + (result.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error saving plan: ' + error.message);
});
};
})
.catch(error => {
alert('Error loading plan: ' + error.message);
});
}
function deleteYearlyPlan(farmId) {
if (!confirm('Are you sure you want to delete the yearly plan for this farm?')) {
return;
}
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/farm/${farmId}/yearly_plan/delete`, {method: 'POST'})
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
alert('Yearly plan deleted successfully!');
location.reload();
} else {
alert('Failed to delete plan: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error deleting plan: ' + error.message);
});
}
function deleteFarm(farmId) {
if (!confirm('Are you sure you want to delete this farm? This will remove all associated data including activities, advisories, and yearly plans. This action cannot be undone.')) {
return;
}
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/farm/${farmId}/delete`, {method: 'POST'})
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
alert('Farm deleted successfully!');
location.reload();
} else {
alert('Failed to delete farm: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error deleting farm: ' + error.message);
});
}
function generateNewAdvisory(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/generate_advisory/${farmId}`)
.then(response => {
if (!response.ok) {
return response.text().then(text => { throw new Error(text || 'Server error'); });
}
return response.json();
})
.then(data => {
modal.hide();
if (data && data.success) {
alert('Daily advisory generated successfully!');
location.reload();
} else {
alert('Failed to generate advisory: ' + (data && data.error ? data.error : 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error generating advisory: ' + (error && error.message ? error.message : String(error)));
});
}
function sendSMSAdvisory(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/send_sms_advisory/${farmId}`)
.then(response => {
if (!response.ok) {
return response.text().then(text => { throw new Error(text || 'Server error'); });
}
return response.json();
})
.then(data => {
modal.hide();
if (data && data.success) {
alert('SMS sent successfully!');
} else {
alert('Failed to send SMS: ' + (data && data.error ? data.error : 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error sending SMS: ' + (error && error.message ? error.message : String(error)));
});
}
function sendTelegramAdvisory(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/send_telegram_advisory/${farmId}`)
.then(response => {
if (!response.ok) {
return response.text().then(text => { throw new Error(text || 'Server error'); });
}
return response.json();
})
.then(data => {
modal.hide();
if (data && data.success) {
alert('Telegram message sent successfully!');
} else {
alert('Failed to send Telegram message: ' + (data && data.error ? data.error : 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error sending Telegram message: ' + (error && error.message ? error.message : String(error)));
});
}
function checkWeather(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/api/weather/${farmId}`)
.then(response => {
if (!response.ok) {
return response.text().then(text => { throw new Error(text || 'Server error'); });
}
return response.json();
})
.then(data => {
modal.hide();
if (data.error) {
alert('Weather data unavailable: ' + data.error);
} else {
let weatherInfo = `Current Weather:\n`;
weatherInfo += `Temperature: ${data.main?.temp || 'N/A'}Β°C\n`;
weatherInfo += `Humidity: ${data.main?.humidity || 'N/A'}%\n`;
weatherInfo += `Condition: ${data.weather?.[0]?.description || 'N/A'}\n`;
weatherInfo += `Wind: ${data.wind?.speed ? (data.wind.speed * 3.6).toFixed(1) : 'N/A'} km/h\n`;
alert(weatherInfo);
}
})
.catch(error => {
modal.hide();
alert('Error fetching weather: ' + (error && error.message ? error.message : String(error)));
});
}
// New feature functions
function downloadYearlyPlanPDF(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/farm/${farmId}/yearly_plan/pdf`)
.then(response => {
modal.hide();
if (response.ok) {
// Trigger download
const link = document.createElement('a');
link.href = `/farmer/farm/${farmId}/yearly_plan/pdf`;
link.download = `yearly_plan_farm_${farmId}.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
alert('Failed to generate PDF. Please ensure you have a yearly plan first.');
}
})
.catch(error => {
modal.hide();
alert('Error generating PDF: ' + error.message);
});
}
function sendYearlyPlanTelegram(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/farm/${farmId}/yearly_plan/send_telegram`, {
method: 'POST',
headers: {'Content-Type': 'application/json'}
})
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
alert('βœ… ' + data.message);
} else {
alert('❌ ' + (data.error || 'Failed to send via Telegram'));
}
})
.catch(error => {
modal.hide();
alert('Error sending via Telegram: ' + error.message);
});
}
function viewWeatherAlerts(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/weather_alerts/${farmId}`)
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
let alertsHtml = '<h5><i class="fas fa-cloud-rain me-2"></i>Weather Alerts</h5>';
if (data.alerts && data.alerts.length > 0) {
alertsHtml += '<div class="list-group">';
data.alerts.forEach(alert => {
const severityClass = alert.severity === 'high' ? 'danger' :
alert.severity === 'medium' ? 'warning' : 'info';
alertsHtml += `
<div class="list-group-item list-group-item-${severityClass}">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">${alert.alert_type}</h6>
<small>${new Date(alert.created_at).toLocaleDateString()}</small>
</div>
<p class="mb-1">${alert.message}</p>
<small>Severity: <span class="badge bg-${severityClass}">${alert.severity}</span></small>
</div>
`;
});
alertsHtml += '</div>';
alertsHtml += `
<div class="mt-3">
<button class="btn btn-primary" onclick="enableWeatherAlerts(${farmId})">
<i class="fas fa-bell me-2"></i>Enable Alerts
</button>
<button class="btn btn-danger" onclick="disableWeatherAlerts(${farmId})">
<i class="fas fa-bell-slash me-2"></i>Disable Alerts
</button>
</div>
`;
} else {
alertsHtml += '<p class="text-muted">No weather alerts at this time.</p>';
alertsHtml += `
<button class="btn btn-primary" onclick="enableWeatherAlerts(${farmId})">
<i class="fas fa-bell me-2"></i>Enable Weather Alerts
</button>
`;
}
_showYearlyPlanModal(alertsHtml);
} else {
alert('Failed to load weather alerts: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error loading weather alerts: ' + error.message);
});
}
function enableWeatherAlerts(farmId) {
fetch(`/farmer/weather_alerts/${farmId}`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({action: 'enable'})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Weather alerts enabled successfully!');
viewWeatherAlerts(farmId); // Refresh the view
} else {
alert('Failed to enable alerts: ' + (data.error || 'Unknown error'));
}
})
.catch(error => alert('Error: ' + error.message));
}
function disableWeatherAlerts(farmId) {
fetch(`/farmer/weather_alerts/${farmId}`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({action: 'disable'})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Weather alerts disabled successfully!');
viewWeatherAlerts(farmId); // Refresh the view
} else {
alert('Failed to disable alerts: ' + (data.error || 'Unknown error'));
}
})
.catch(error => alert('Error: ' + error.message));
}
function viewMarketPrices(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/market_prices`)
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
let pricesHtml = '<h5><i class="fas fa-chart-line me-2"></i>Market Prices</h5>';
if (data.prices && data.prices.length > 0) {
pricesHtml += '<div class="table-responsive">';
pricesHtml += '<table class="table table-striped">';
pricesHtml += '<thead><tr><th>Crop</th><th>Market</th><th>Price</th><th>Trend</th><th>Date</th></tr></thead><tbody>';
data.prices.forEach(price => {
const trendIcon = price.trend === 'up' ? 'πŸ“ˆ' : price.trend === 'down' ? 'πŸ“‰' : '➑️';
pricesHtml += `
<tr>
<td><span class="badge bg-success">${price.crop_type}</span></td>
<td>${price.market_name}</td>
<td>β‚Ή${price.price_per_unit}/${price.unit}</td>
<td>${trendIcon} ${price.trend}</td>
<td>${new Date(price.date).toLocaleDateString()}</td>
</tr>
`;
});
pricesHtml += '</tbody></table></div>';
pricesHtml += `
<div class="mt-3">
<button class="btn btn-primary" onclick="refreshMarketPrices(${farmId})">
<i class="fas fa-sync me-2"></i>Refresh Prices
</button>
<button class="btn btn-success" onclick="subscribeToMarketAlerts(${farmId})">
<i class="fas fa-bell me-2"></i>Subscribe to Alerts
</button>
</div>
`;
} else {
pricesHtml += '<p class="text-muted">No market price data available.</p>';
pricesHtml += `
<button class="btn btn-primary" onclick="refreshMarketPrices(${farmId})">
<i class="fas fa-sync me-2"></i>Fetch Market Prices
</button>
`;
}
_showYearlyPlanModal(pricesHtml);
} else {
alert('Failed to load market prices: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error loading market prices: ' + error.message);
});
}
function refreshMarketPrices(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/market_prices`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({action: 'refresh'})
})
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
alert('Market prices updated successfully!');
viewMarketPrices(farmId); // Refresh the view
} else {
alert('Failed to refresh prices: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error refreshing prices: ' + error.message);
});
}
function subscribeToMarketAlerts(farmId) {
fetch(`/farmer/market_prices`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({action: 'subscribe'})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Subscribed to market price alerts successfully!');
} else {
alert('Failed to subscribe: ' + (data.error || 'Unknown error'));
}
})
.catch(error => alert('Error: ' + error.message));
}
function detectDisease(farmId) {
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/disease_detection/${farmId}`)
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
let diseaseHtml = '<h5><i class="fas fa-bug me-2"></i>Disease Detection</h5>';
if (data.detections && data.detections.length > 0) {
diseaseHtml += '<div class="row">';
data.detections.forEach(detection => {
const severityClass = detection.severity === 'high' ? 'danger' :
detection.severity === 'medium' ? 'warning' : 'success';
diseaseHtml += `
<div class="col-md-6 mb-3">
<div class="card border-${severityClass}">
<div class="card-header bg-${severityClass} text-white">
<h6 class="mb-0">${detection.disease_name}</h6>
</div>
<div class="card-body">
<p><strong>Confidence:</strong> ${Math.round(detection.confidence * 100)}%</p>
<p><strong>Severity:</strong> <span class="badge bg-${severityClass}">${detection.severity}</span></p>
<p><strong>Treatment:</strong> ${detection.treatment_recommendation}</p>
<small class="text-muted">Detected: ${new Date(detection.detection_date).toLocaleDateString()}</small>
</div>
</div>
</div>
`;
});
diseaseHtml += '</div>';
} else {
diseaseHtml += '<p class="text-muted">No disease detections recorded.</p>';
}
diseaseHtml += `
<div class="mt-3">
<label for="diseaseImageUpload" class="btn btn-primary">
<i class="fas fa-camera me-2"></i>Upload Crop Image
</label>
<input type="file" id="diseaseImageUpload" accept="image/*" style="display: none;" onchange="uploadDiseaseImage(${farmId}, this)">
<button class="btn btn-success ms-2" onclick="viewDiseaseHistory(${farmId})">
<i class="fas fa-history me-2"></i>View History
</button>
</div>
`;
_showYearlyPlanModal(diseaseHtml);
} else {
alert('Failed to load disease detection data: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error loading disease detection: ' + error.message);
});
}
function uploadDiseaseImage(farmId, input) {
if (input.files && input.files[0]) {
const formData = new FormData();
formData.append('image', input.files[0]);
const modal = _getLoadingModalInstance();
modal.show();
fetch(`/farmer/disease_detection/${farmId}`, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
modal.hide();
if (data.success) {
alert('Image uploaded and analyzed successfully!');
detectDisease(farmId); // Refresh the view
} else {
alert('Failed to analyze image: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
modal.hide();
alert('Error uploading image: ' + error.message);
});
}
}
function viewDiseaseHistory(farmId) {
window.open(`/farmer/disease_detection/${farmId}`, '_blank');
}
function showSendImageModal() {
const modalHtml = `
<div class="modal fade" id="sendImageModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-paper-plane me-2"></i>Send Image via Telegram</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="sendImageForm" enctype="multipart/form-data">
<div class="mb-3">
<label for="imageFile" class="form-label">Select Image:</label>
<input type="file" class="form-control" id="imageFile" name="image" accept="image/*" required>
<div class="form-text">Supported formats: JPG, PNG, GIF, BMP</div>
</div>
<div class="mb-3">
<label for="imageCaption" class="form-label">Caption (optional):</label>
<textarea class="form-control" id="imageCaption" name="caption" rows="3" placeholder="Add a description for your image..."></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="sendImageTelegram()">
<i class="fas fa-paper-plane me-2"></i>Send via Telegram
</button>
</div>
</div>
</div>
</div>
`;
// Remove existing modal if any
const existingModal = document.getElementById('sendImageModal');
if (existingModal) {
existingModal.remove();
}
// Add modal to body
document.body.insertAdjacentHTML('beforeend', modalHtml);
// Show modal
const modal = new bootstrap.Modal(document.getElementById('sendImageModal'));
modal.show();
}
function sendImageTelegram() {
const form = document.getElementById('sendImageForm');
const fileInput = document.getElementById('imageFile');
const captionInput = document.getElementById('imageCaption');
if (!fileInput.files[0]) {
alert('Please select an image file');
return;
}
const formData = new FormData();
formData.append('image', fileInput.files[0]);
formData.append('caption', captionInput.value || `πŸ“· Image from {{ current_user.name if current_user else 'Farmer' }}`);
const modal = bootstrap.Modal.getInstance(document.getElementById('sendImageModal'));
modal.hide();
const loadingModal = _getLoadingModalInstance();
loadingModal.show();
fetch('/farmer/send_image_telegram', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
loadingModal.hide();
if (data.success) {
alert('βœ… ' + data.message);
} else {
alert('❌ ' + (data.error || 'Failed to send image via Telegram'));
}
})
.catch(error => {
loadingModal.hide();
alert('Error sending image: ' + error.message);
});
}
// ==================== DAILY TASKS FUNCTIONS ====================
function loadDailyTasks(date = null) {
const targetDate = date || new Date().toISOString().split('T')[0];
const loadingSpinner = '<div class="text-center py-4"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div><p class="mt-2">Loading daily tasks...</p></div>';
document.getElementById('daily-tasks-container').innerHTML = loadingSpinner;
document.getElementById('task-date').textContent = targetDate;
fetch(`/farmer/daily_tasks?date=${targetDate}`)
.then(response => response.json())
.then(data => {
if (data.success) {
renderDailyTasks(data.tasks);
} else {
showNoTasksMessage('Failed to load tasks: ' + (data.error || 'Unknown error'));
}
})
.catch(error => {
console.error('Error loading daily tasks:', error);
showNoTasksMessage('Error loading tasks. Please try again.');
});
}
function renderDailyTasks(tasks) {
const container = document.getElementById('daily-tasks-container');
if (!tasks || tasks.length === 0) {
showNoTasksMessage();
return;
}
let html = '<div class="row">';
tasks.forEach((task, index) => {
const priorityClass = task.priority === 'high' ? 'danger' : task.priority === 'medium' ? 'warning' : 'success';
const priorityIcon = task.priority === 'high' ? 'πŸ”΄' : task.priority === 'medium' ? '🟑' : '🟒';
const completedClass = task.is_completed ? 'border-success bg-light' : '';
const completedIcon = task.is_completed ? 'βœ…' : '⏳';
html += `
<div class="col-md-6 col-lg-4 mb-3">
<div class="card h-100 ${completedClass}">
<div class="card-header d-flex justify-content-between align-items-center">
<small class="text-${priorityClass}">
${priorityIcon} ${task.priority.toUpperCase()} Priority
</small>
<small>${completedIcon} ${task.estimated_duration} min</small>
</div>
<div class="card-body">
<h6 class="card-title">${task.task_title}</h6>
<p class="card-text small">${task.task_description}</p>
${task.crop_specific ? `<span class="badge bg-info mb-2">🌾 ${task.crop_specific}</span>` : ''}
${task.weather_dependent ? '<span class="badge bg-warning mb-2">🌀️ Weather Dependent</span>' : ''}
</div>
<div class="card-footer bg-transparent">
<div class="d-grid gap-1">
${task.is_completed ? `
<button class="btn btn-outline-secondary btn-sm" onclick="uncompleteTask(${task.id})">
<i class="fas fa-undo me-1"></i>Mark as Incomplete
</button>
${task.rating ? `<small class="text-muted">Rating: ${'⭐'.repeat(task.rating)}</small>` : ''}
` : `
<button class="btn btn-success btn-sm" onclick="completeTask(${task.id})">
<i class="fas fa-check me-1"></i>Mark as Complete
</button>
`}
</div>
</div>
</div>
</div>`;
});
html += '</div>';
container.innerHTML = html;
}
function showNoTasksMessage(message = null) {
const defaultMessage = `
<div class="text-center text-muted py-4" id="no-tasks-message">
<i class="fas fa-clipboard-list fa-3x mb-3"></i>
<h6>No daily tasks available</h6>
<p>Generate daily tasks to get AI-powered farming recommendations</p>
</div>`;
const errorMessage = `
<div class="text-center text-danger py-4">
<i class="fas fa-exclamation-triangle fa-3x mb-3"></i>
<h6>Error Loading Tasks</h6>
<p>${message}</p>
</div>`;
document.getElementById('daily-tasks-container').innerHTML = message ? errorMessage : defaultMessage;
}
function generateDailyTasks() {
const today = new Date().toISOString().split('T')[0];
const loadingSpinner = '<div class="text-center py-4"><div class="spinner-border text-success" role="status"><span class="visually-hidden">Generating...</span></div><p class="mt-2">Generating daily tasks with AI...</p></div>';
document.getElementById('daily-tasks-container').innerHTML = loadingSpinner;
fetch('/farmer/daily_tasks/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
date: today
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('βœ… ' + data.message);
loadDailyTasks(); // Reload tasks to show the generated ones
} else {
alert('❌ ' + (data.error || 'Failed to generate daily tasks'));
showNoTasksMessage();
}
})
.catch(error => {
console.error('Error generating daily tasks:', error);
alert('Error generating daily tasks. Please try again.');
showNoTasksMessage();
});
}
function completeTask(taskId) {
// Show rating modal
const rating = prompt('Rate this task completion (1-5 stars):');
const feedback = prompt('Any feedback about this task? (optional):');
let ratingNum = null;
if (rating && !isNaN(rating)) {
ratingNum = Math.max(1, Math.min(5, parseInt(rating)));
}
fetch(`/farmer/daily_tasks/${taskId}/complete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
rating: ratingNum,
feedback: feedback || ''
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('βœ… Task completed successfully!');
loadDailyTasks(); // Reload tasks to update status
} else {
alert('❌ ' + (data.error || 'Failed to complete task'));
}
})
.catch(error => {
console.error('Error completing task:', error);
alert('Error completing task. Please try again.');
});
}
function uncompleteTask(taskId) {
if (!confirm('Are you sure you want to mark this task as incomplete?')) {
return;
}
fetch(`/farmer/daily_tasks/${taskId}/uncomplete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('βœ… Task marked as incomplete');
loadDailyTasks(); // Reload tasks to update status
} else {
alert('❌ ' + (data.error || 'Failed to uncomplete task'));
}
})
.catch(error => {
console.error('Error uncompleting task:', error);
alert('Error updating task. Please try again.');
});
}
function sendTasksTelegram() {
const today = new Date().toISOString().split('T')[0];
fetch('/farmer/daily_tasks/send_telegram', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
date: today
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('βœ… ' + data.message);
} else {
alert('❌ ' + (data.error || 'Failed to send tasks via Telegram'));
}
})
.catch(error => {
console.error('Error sending tasks via Telegram:', error);
alert('Error sending tasks. Please try again.');
});
}
function deleteAllTasks() {
if (!confirm('Are you sure you want to delete all tasks for today? This action cannot be undone.')) {
return;
}
const today = new Date().toISOString().split('T')[0];
fetch('/farmer/daily_tasks/delete_all', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
date: today
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('βœ… ' + data.message);
showNoTasksMessage(); // Show the no tasks message
} else {
alert('❌ ' + (data.error || 'Failed to delete tasks'));
}
})
.catch(error => {
console.error('Error deleting tasks:', error);
alert('Error deleting tasks. Please try again.');
});
}
// Auto-load today's tasks when page loads
document.addEventListener('DOMContentLoaded', function() {
// Load today's tasks automatically
loadDailyTasks();
});
</script>
</script>
{% endblock %}