insights: remove EC from rate-of-change visualisation

EC rate of change was declared irrelevant by the farmer. Remove EC from the derivatives chart (At a Glance section) and remove the EC Trend & Rate card from the Explore section.

Commit
8e4ce34af91385240c76b75befe923282a72d858
Author
GPT-5 medium <codex@openai.com>
Author date
Committer
GPT-5 medium <codex@openai.com>
Committer date
Changed files
roles/dashboard/public/js/dashboard/insights.js
index 7a5b2c45..c2444317 100644..100644
@@ -37,9 +37,11 @@
37 37 ph: READING_RANGES.ph.goodMax - READING_RANGES.ph.goodMin,
38 38 do: READING_RANGES.do.goodMax - READING_RANGES.do.goodMin,
39 39 orp: READING_RANGES.orp.goodMax - READING_RANGES.orp.goodMin,
40 Removed: ec: READING_RANGES.ec.goodMax - READING_RANGES.ec.goodMin,
41 40 };
42 41
42 Added: // Probes shown on the rate-of-change chart (EC excluded per farmer request)
43 Added: const DERIVATIVE_PROBES = ["ph", "do", "orp"];
44 Added:
43 45 let charts = {};
44 46
45 47 async function fetchJson(url, signal) {
@@ -112,11 +114,12 @@
112 114
113 115 const hours = timeframeToHours(timeframe);
114 116 const data = await fetchJson(`/api/insights/derivatives?hours=${hours}`, signal);
115 Removed: const probeKeys = Object.keys(data.probes);
116 Removed: const refProbe = probeKeys.find(p => data.probes[p].length > 0);
117 Added:
118 Added: // Only use the probes we want (no EC)
119 Added: const refProbe = DERIVATIVE_PROBES.find(p => data.probes[p]?.length > 0);
117 120 const labels = refProbe ? data.probes[refProbe].map(p => formatDateTime(p.timestamp)) : [];
118 121
119 Removed: const datasets = probeKeys.map(probe => {
122 Added: const datasets = DERIVATIVE_PROBES.map(probe => {
120 123 const span = OPTIMAL_SPANS[probe] || 1;
121 124 const points = data.probes[probe] || [];
122 125
@@ -174,6 +177,7 @@
174 177 const canvas = document.getElementById("chart-diurnal");
175 178 if (!canvas) return;
176 179
180 Added: // Diurnal always shows day overlay, but we use the timeframe range as days
177 181 const hours = timeframeToHours(timeframe);
178 182 const days = Math.max(1, Math.round(hours / 24));
179 183
@@ -311,89 +315,6 @@
311 315 }
312 316 }
313 317
314 Removed: async function loadEcRate(timeframe, signal) {
315 Removed: const canvas = document.getElementById("chart-ec-rate");
316 Removed: if (!canvas) return;
317 Removed:
318 Removed: const tf = timeframe.selected();
319 Removed: const range = timeframe.range();
320 Removed: const ecData = await fetchProbeData("ec", tf, range, false, signal);
321 Removed:
322 Removed: const labels = ecData.points.map(p => formatChartTime(p.timestamp, tf));
323 Removed: const ecValues = ecData.points.map(p => p.value);
324 Removed:
325 Removed: // Compute rate from the series points directly
326 Removed: const rateValues = ecValues.map((v, i) => {
327 Removed: if (i === 0 || v === null || ecValues[i - 1] === null) return null;
328 Removed: return v - ecValues[i - 1];
329 Removed: });
330 Removed:
331 Removed: if (!charts.ecRate) {
332 Removed: charts.ecRate = new Chart(canvas, {
333 Removed: type: "line",
334 Removed: data: {
335 Removed: labels,
336 Removed: datasets: [
337 Removed: {
338 Removed: label: "EC (µS/cm)",
339 Removed: data: ecValues,
340 Removed: borderColor: PROBE_COLORS.ec.border,
341 Removed: backgroundColor: PROBE_COLORS.ec.background,
342 Removed: tension: 0.2,
343 Removed: pointRadius: 0,
344 Removed: pointHoverRadius: 4,
345 Removed: yAxisID: "yEC",
346 Removed: fill: true
347 Removed: },
348 Removed: {
349 Removed: label: "EC change (Δ)",
350 Removed: data: rateValues,
351 Removed: borderColor: "#f59e0b",
352 Removed: backgroundColor: "rgba(245, 158, 11, 0.1)",
353 Removed: borderDash: [4, 3],
354 Removed: tension: 0.3,
355 Removed: pointRadius: 0,
356 Removed: pointHoverRadius: 3,
357 Removed: yAxisID: "yRate",
358 Removed: fill: false
359 Removed: }
360 Removed: ]
361 Removed: },
362 Removed: options: {
363 Removed: responsive: true,
364 Removed: maintainAspectRatio: false,
365 Removed: animation: false,
366 Removed: interaction: { mode: "index", intersect: false },
367 Removed: scales: {
368 Removed: x: { ticks: { maxTicksLimit: 8, font: { size: 10 } } },
369 Removed: yEC: {
370 Removed: type: "linear",
371 Removed: position: "left",
372 Removed: title: { display: true, text: "EC (µS/cm)", color: PROBE_COLORS.ec.border },
373 Removed: ticks: { color: PROBE_COLORS.ec.border }
374 Removed: },
375 Removed: yRate: {
376 Removed: type: "linear",
377 Removed: position: "right",
378 Removed: title: { display: true, text: "Δ per bucket", color: "#f59e0b" },
379 Removed: ticks: { color: "#f59e0b" },
380 Removed: grid: { drawOnChartArea: false }
381 Removed: }
382 Removed: },
383 Removed: plugins: {
384 Removed: legend: { display: true },
385 Removed: tooltip: { ...TOOLTIP_DEFAULTS, callbacks: { title: () => "" } }
386 Removed: }
387 Removed: }
388 Removed: });
389 Removed: } else {
390 Removed: charts.ecRate.data.labels = labels;
391 Removed: charts.ecRate.data.datasets[0].data = ecValues;
392 Removed: charts.ecRate.data.datasets[1].data = rateValues;
393 Removed: charts.ecRate.update();
394 Removed: }
395 Removed: }
396 Removed:
397 318 async function loadPhDoLag(timeframe, signal) {
398 319 const canvas = document.getElementById("chart-ph-do-lag");
399 320 if (!canvas) return;
@@ -498,7 +419,6 @@
498 419 loadDerivatives(timeframe, signal),
499 420 loadDiurnal(timeframe, signal),
500 421 loadDoOrpOverlay(timeframe, signal),
501 Removed: loadEcRate(timeframe, signal),
502 422 loadPhDoLag(timeframe, signal)
503 423 ]);
504 424 }
roles/dashboard/t/08-insights.t
index a47d91c4..64cad1bd 100644..100644
@@ -36,7 +36,6 @@
36 36 ->element_exists('canvas#chart-derivatives')
37 37 ->element_exists('canvas#chart-diurnal')
38 38 ->element_exists('canvas#chart-overlay-do-orp')
39 Removed: ->element_exists('canvas#chart-ec-rate')
40 39 ->element_exists('canvas#chart-ph-do-lag');
41 40 };
42 41
roles/dashboard/templates/dashboard/insights.html.ep
index 464112f7..b43c15db 100644..100644
@@ -106,22 +106,6 @@
106 106 </div>
107 107 </div>
108 108
109 Removed: <div class="col-12" id="ec-rate">
110 Removed: <div class="card">
111 Removed: <div class="card-header">
112 Removed: <h3 class="h6 mb-0">EC Trend &amp; Rate</h3>
113 Removed: <p class="text-body-secondary small mb-0 mt-1">
114 Removed: Electrical conductivity with rate of change &mdash; rising EC means nutrient buildup
115 Removed: </p>
116 Removed: </div>
117 Removed: <div class="card-body">
118 Removed: <div class="chart-wrap">
119 Removed: <canvas id="chart-ec-rate" role="img" aria-label="EC trend with rate of change"></canvas>
120 Removed: </div>
121 Removed: </div>
122 Removed: </div>
123 Removed: </div>
124 Removed:
125 109 <div class="col-12" id="ph-do-lag">
126 110 <div class="card">
127 111 <div class="card-header">