import SwiftUI struct OLTDetailView: View { let connection: MCMSConnection @State private var vm: OLTDetailViewModel @State private var showResetConfirm = false private let title: String init(connection: MCMSConnection, oltId: String, title: String? = nil) { self.connection = connection self.title = title ?? oltId _vm = State(initialValue: OLTDetailViewModel(connection: connection, oltId: oltId)) } var body: some View { Group { switch vm.phase { case .idle: LoadingView() case .loading where vm.olt == nil: LoadingView() case .failed(let msg): ErrorStateView(message: msg) { Task { await vm.load() } } default: content } } .navigationTitle(title) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Menu { Button(role: .destructive) { showResetConfirm = true } label: { Label("Reset OLT", systemImage: "arrow.clockwise") } } label: { Image(systemName: "ellipsis.circle") } .disabled(vm.isPerformingAction) } } .sheet(isPresented: $showResetConfirm) { ResetConfirmSheet(deviceKind: "OLT", deviceName: title) { Task { await vm.reset() } } } .alert("Operation", isPresented: Binding( get: { vm.actionMessage != nil }, set: { if !$0 { vm.actionMessage = nil } } )) { Button("OK") { vm.actionMessage = nil } } message: { Text(vm.actionMessage ?? "") } .task { if vm.phase == .idle { await vm.load() } } .refreshable { await vm.load() } } @ViewBuilder private var content: some View { if let olt = vm.olt { List { Section("Status") { LabeledContent("ONUs", value: onuSummary(olt)) if let model = olt.model { LabeledContent("Model", value: model) } if let mode = olt.ponMode { LabeledContent("PON mode", value: mode) } if let fw = olt.firmwareVersion { LabeledContent("Firmware", value: fw) } if let online = olt.onlineTime { if let up = MongoTimestamp.duration(from: online, to: olt.lastUpdate) { LabeledContent("Uptime", value: up) } LabeledContent("Online since", value: MongoTimestamp.displayString(online) ?? online) } if let updated = MongoTimestamp.displayString(olt.lastUpdate) { LabeledContent("Last update", value: updated) } } if olt.rxRateBps != nil || olt.txRateBps != nil || olt.txUtilPercent != nil { Section("PON traffic") { LabeledContent("Downstream", value: "\(Format.bps(olt.txRateBps)) · \(Format.percent(olt.txUtilPercent)) util") LabeledContent("Upstream", value: "\(Format.bps(olt.rxRateBps)) · \(Format.percent(olt.rxUtilPercent)) util") } } if olt.asicTempC != nil || olt.voltage != nil { Section("Environment") { if let temp = olt.asicTempC { LabeledContent("ASIC temp", value: "\(temp) °C") } if let volts = olt.voltage { LabeledContent("Voltage", value: "\(volts.formatted(.number.precision(.fractionLength(2)))) V") } } } if let switchName = olt.switchName { Section("Uplink") { LabeledContent("Switch", value: switchName) if let port = olt.switchPort { LabeledContent("Port", value: port) } if let addr = olt.switchAddress { LabeledContent("Address", value: addr) } } } if olt.populatedBuckets.isEmpty { Section { Text("No ONUs registered on this OLT.").foregroundStyle(.secondary) } } else { ForEach(olt.populatedBuckets, id: \.state) { bucket in Section { ForEach(bucket.ids, id: \.self) { onuId in NavigationLink { ONUDetailView(connection: connection, onuId: onuId) } label: { Text(onuId) } } } header: { HStack { StateBadge(text: bucket.state.label, color: bucket.state.color) Spacer() Text("\(bucket.ids.count)") .font(.caption.monospacedDigit()) .foregroundStyle(.secondary) } } } } Section("Raw document") { JSONTreeView(value: olt.raw) } } } } private func onuSummary(_ olt: OLTStateDoc) -> String { let down = olt.totalONUs - olt.onlineONUs var summary = "\(olt.totalONUs) · \(olt.onlineONUs) online" if down > 0 { summary += " · \(down) down" } return summary } }