No data yet.
`;
}
card.classList.add(statusClass);
plotsGrid.appendChild(card);
});
if(alertCount === 0) alertsList.innerHTML = '
✅ No urgent alerts. All systems normal.';
// Chart
const twentyFourHoursAgo = dateFns.subHours(currentTime, 24);
const trendData = state.sensorLog.filter(l => l.timestamp >= twentyFourHoursAgo);
const datasets = state.plots.map((plot, i) => {
const colors = ['#2980b9', '#27ae60', '#f39c12'];
return {
label: plot.name,
data: trendData.filter(l => l.plotId === plot.id).map(l => ({ x: l.timestamp, y: l.soilMoisture })),
borderColor: colors[i % colors.length],
tension: 0.1
}
});
charts.moisture = new Chart(document.getElementById('moistureChart').getContext('2d'), {
type: 'line', data: { datasets },
options: { responsive: true, maintainAspectRatio: false, scales: { x: { type: 'time', time: { unit: 'hour' } }, y: { title: { display: true, text: 'Soil Moisture (%)' } } } }
});
};
const renderManagementTab = () => {
// Render Plots Management Table
const plotManager = document.getElementById('plots-manager');
let plotTable = `
| Plot Name | Plant Type | Moisture Min/Max | Actions |
`;
state.plots.forEach(p => {
plotTable += `| ${p.name} | ${p.plantType} | ${p.settings.minMoisture}% / ${p.settings.maxMoisture}% |
|
`;
});
plotManager.innerHTML = plotTable + '
';
// Render Log Table
const logBody = document.getElementById('log-tbody');
logBody.innerHTML = '';
state.sensorLog.sort((a,b)=>b.timestamp-a.timestamp).forEach(log => {
const plot = state.plots.find(p => p.id === log.plotId);
const row = document.createElement('tr');
row.innerHTML = `
${log.timestamp.toLocaleString()} | ${plot?.name || 'N/A'} | ${log.soilMoisture}% | ${log.temperature}°C |
| `;
logBody.appendChild(row);
});
// Populate dropdown
logForm.plot.innerHTML = state.plots.map(p => `
`).join('');
};
// --- EVENT LISTENERS ---
plotForm.addEventListener('submit', (e) => {
e.preventDefault();
const newPlot = {
id: Date.now(),
name: document.getElementById('plot-name').value,
plantType: document.getElementById('plant-type').value,
settings: {
minMoisture: parseInt(document.getElementById('min-moisture').value),
maxMoisture: parseInt(document.getElementById('max-moisture').value)
}
};
state.plots.push(newPlot);
saveState(); renderAll(); plotForm.reset();
});
logForm.form.addEventListener('submit', (e) => {
e.preventDefault();
const newLog = {
id: Date.now(), timestamp: new Date(logForm.timestamp.value),
plotId: parseInt(logForm.plot.value), soilMoisture: parseInt(logForm.moisture.value),
temperature: parseInt(logForm.temp.value)
};
state.sensorLog.push(newLog);
saveState(); renderAll(); logForm.form.reset();
});
window.handleDelete = (stateKey, id) => {
if(confirm(`Are you sure you want to delete this ${stateKey === 'plots' ? 'plot' : 'log entry'}?`)) {
state[stateKey] = state[stateKey].filter(item => item.id !== id);
saveState();
renderAll();
}
};
window.gardenSwitchTab = (evt, tabName) => {
document.querySelectorAll('#garden-dashboard-container .garden-tab-content').forEach(tc => tc.style.display = 'none');
document.querySelectorAll('#garden-dashboard-container .garden-tab-button').forEach(tl => tl.classList.remove('active'));
document.getElementById(tabName).style.display = 'block';
if (tabName === 'data') renderAll(); // Rerender management tab when clicked
};
// --- INITIALIZATION ---
loadState();
renderAll();
});