fix(web): fetch all unit runtimes on page load
Root cause: state.runtimes was empty after refresh because the engine
only pushes UnitRuntimeChanged on state transitions — if the engine
is mid-wait-phase, no push occurs and badges show OFFLINE.
Fix: add GET /api/unit/runtimes batch endpoint (returns all known
runtimes as { unit_id: UnitRuntime }) and call it in parallel with
the unit list fetch inside loadUnits(), so runtime badges are correct
immediately after page load.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b3f92867bc
commit
42cdbbc0cc
|
|
@ -81,6 +81,10 @@ impl ControlRuntimeStore {
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_all(&self) -> HashMap<Uuid, UnitRuntime> {
|
||||||
|
self.inner.read().await.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Wake the engine task for a unit (e.g., when auto_enabled or fault_locked changes).
|
/// Wake the engine task for a unit (e.g., when auto_enabled or fault_locked changes).
|
||||||
pub async fn notify_unit(&self, unit_id: Uuid) {
|
pub async fn notify_unit(&self, unit_id: Uuid) {
|
||||||
if let Some(n) = self.notifiers.read().await.get(&unit_id) {
|
if let Some(n) = self.notifiers.read().await.get(&unit_id) {
|
||||||
|
|
|
||||||
|
|
@ -509,3 +509,11 @@ pub async fn get_unit_runtime(
|
||||||
let runtime = state.control_runtime.get_or_init(unit_id).await;
|
let runtime = state.control_runtime.get_or_init(unit_id).await;
|
||||||
Ok(Json(runtime))
|
Ok(Json(runtime))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns all known runtimes as { unit_id: UnitRuntime }.
|
||||||
|
/// Used by the frontend on page load to populate initial state.
|
||||||
|
pub async fn get_all_unit_runtimes(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
Json(state.control_runtime.get_all().await)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,10 @@ fn build_router(state: AppState) -> Router {
|
||||||
"/api/unit",
|
"/api/unit",
|
||||||
get(handler::control::get_unit_list).post(handler::control::create_unit),
|
get(handler::control::get_unit_list).post(handler::control::create_unit),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/api/unit/runtimes",
|
||||||
|
get(handler::control::get_all_unit_runtimes),
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/api/unit/{unit_id}",
|
"/api/unit/{unit_id}",
|
||||||
get(handler::control::get_unit)
|
get(handler::control::get_unit)
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,10 @@ export function renderUnits() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadUnits() {
|
export async function loadUnits() {
|
||||||
const response = await apiFetch("/api/unit?page=1&page_size=-1");
|
const [response, runtimes] = await Promise.all([
|
||||||
|
apiFetch("/api/unit?page=1&page_size=-1"),
|
||||||
|
apiFetch("/api/unit/runtimes").catch(() => ({})),
|
||||||
|
]);
|
||||||
state.units = response.data || [];
|
state.units = response.data || [];
|
||||||
state.unitMap = new Map(state.units.map((unit) => [unit.id, unit]));
|
state.unitMap = new Map(state.units.map((unit) => [unit.id, unit]));
|
||||||
|
|
||||||
|
|
@ -188,6 +191,10 @@ export async function loadUnits() {
|
||||||
state.selectedUnitId = null;
|
state.selectedUnitId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const [unitId, runtime] of Object.entries(runtimes)) {
|
||||||
|
state.runtimes.set(unitId, runtime);
|
||||||
|
}
|
||||||
|
|
||||||
renderUnits();
|
renderUnits();
|
||||||
renderUnitOptions(dom.equipmentUnitId?.value || "", dom.equipmentUnitId);
|
renderUnitOptions(dom.equipmentUnitId?.value || "", dom.equipmentUnitId);
|
||||||
renderUnitOptions(dom.equipmentBatchUnitId?.value || "", dom.equipmentBatchUnitId);
|
renderUnitOptions(dom.equipmentBatchUnitId?.value || "", dom.equipmentBatchUnitId);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue