{/* Header */}
{sections.map((s) => (
setSection(s.key)} />
))}
{batteries.length} batteries loaded
{/* ── FLEET OVERVIEW ── */}
{section === "fleet" && (
{/* Fleet status cards */}
Healthy (≥85%)
{fleetStats.healthy}
Near EOL (<70%)
{fleetStats.eol}
{/* Filters */}
{/* Fleet SOH bar chart */}
Fleet SOH — Sorted (Top 25)
[`${v}%`, "SOH"]} />
{fleetBarData.map((d, i) => (
= 85 ? "#22c55e" : d.soh >= 70 ? "#f59e0b" : "#ef4444"} />
))}
|
{/* Fleet scatter */}
SOH vs Cycles (bubble size = temperature)
payload?.length ? (
{payload[0].payload.name}
SOH: {payload[0].payload.y}%
Cycles: {payload[0].payload.x}
Temp: {payload[0].payload.z}°C
) : null}
/>
{/* Battery table */}
Battery Roster
| ID |
SOH |
Cycles |
Temp °C |
State |
{filteredBatteries.map((b, i) => (
{ setSelectedBat(b.battery_id); setSection("single"); }}
>
| {b.battery_id} |
|
{b.n_cycles ?? "—"} |
{(b.ambient_temperature ?? b.avg_temperature ?? "—")} |
{b.degradation_state ?? "—"}
|
))}
)}
{/* ── SINGLE BATTERY ── */}
{section === "single" && (
{loadingSingle &&
}
{/* SOH + RUL projection */}
SOH Trajectory + RUL Projection
[`${v}%`, name]} />
{showEol && }
{rulProjection.length > 0 && (
)}
{/* Capacity fade */}
Capacity Fade (Ah)
[`${v} Ah`, "Capacity"]} />
{showEol && }
{/* Degradation rate */}
Degradation Rate (SOH %/cycle, smoothed)
[`${v}%/cyc`, "Deg Rate"]} />
)}
{/* ── COMPARE ── */}
{section === "compare" && (
Select up to 5 batteries to compare
{loadingCompare &&
}
{batteries.map((b) => {
const selected = compareIds.includes(b.battery_id);
return (
);
})}
{compareIds.length === 0 ? (
Select batteries above to compare
) : (
{/* SOH overlay */}
{compareIds.map((id, i) => {
const d = compareData[id];
if (!d) return null;
const lineData = d.cycles.map((c, j) => ({ cycle: c, soh: d.soh_pct[j] }));
return (
);
})}
{/* Capacity overlay */}
Capacity Fade Comparison
{compareIds.map((id, i) => {
const d = compareData[id];
if (!d) return null;
const lineData = d.cycles.map((c, j) => ({ cycle: c, capacity: d.capacity_ah[j] }));
return (
);
})}
{/* Summary comparison table */}
Comparison Summary
| Battery |
Final SOH |
Cycles |
Min Capacity |
{compareIds.map((id, i) => {
const d = compareData[id];
const lastSoh = d?.soh_pct[d.soh_pct.length - 1];
const minCap = d ? Math.min(...d.capacity_ah) : null;
return (
|
{id}
|
{lastSoh != null ? : "—"}
|
{d?.cycles.length ?? "—"} |
{minCap != null ? `${minCap.toFixed(3)} Ah` : "—"} |
);
})}
)}
)}
{/* ── TEMPERATURE ANALYSIS ── */}
{section === "temperature" && (
Temperature vs Final SOH
payload?.length ? (
{payload[0].payload.name}
Temp: {payload[0].payload.temp}°C
SOH: {payload[0].payload.soh}%
) : null}
/>
{/* Temperature distribution histogram */}
Temperature Distribution
{
const bins: Record = {};
batteries.forEach((b) => {
const t = Math.round((b.ambient_temperature ?? b.avg_temperature ?? 25) / 5) * 5;
bins[t] = (bins[t] ?? 0) + 1;
});
return Object.entries(bins).sort(([a], [b]) => +a - +b).map(([t, count]) => ({ temp: `${t}°C`, count }));
})()}>
)}
);
}