import Foundation import Observation @MainActor @Observable final class ONUListViewModel { var phase: LoadPhase = .idle var onus: [ONUStateDoc] = [] var search = "" /// When set, only ONUs in this registration state are shown (dashboard drill-down). let stateFilter: ONULifecycleState? private let connection: MCMSConnection init(connection: MCMSConnection, stateFilter: ONULifecycleState? = nil) { self.connection = connection self.stateFilter = stateFilter } var filtered: [ONUStateDoc] { var result = onus if let stateFilter { result = result.filter { $0.lifecycle == stateFilter } } let query = search.trimmingCharacters(in: .whitespaces) guard !query.isEmpty else { return result } return result.filter { onu in onu.id.localizedCaseInsensitiveContains(query) || (onu.resolvedName?.localizedCaseInsensitiveContains(query) ?? false) || (onu.parentOLT?.localizedCaseInsensitiveContains(query) ?? false) } } func load() async { phase = .loading do { let fetched = try await connection.onu.allStates() // Registration lives on OLT-STATE, operator names on ONU-CFG. Both best-effort. let registration = (try? await connection.olt.registrationMap()) ?? [:] let names = (try? await connection.onu.nameMap()) ?? [:] onus = fetched .map { onu in var onu = onu onu.resolvedLifecycle = registration[onu.id] onu.resolvedName = names[onu.id] return onu } .sorted { $0.displayName.localizedCaseInsensitiveCompare($1.displayName) == .orderedAscending } phase = .loaded } catch { phase = .failed((error as? APIError)?.localizedDescription ?? error.localizedDescription) } } }