// // PowerbankDetailView.swift // USB Meter // import SwiftUI struct PowerbankDetailView: View { @EnvironmentObject private var appData: AppData @Environment(\.dismiss) private var dismiss let powerbankID: UUID @State private var isEditing = false @State private var pendingDeletion = false private var powerbank: PowerbankSummary? { appData.powerbankSummaries.first { $0.id == powerbankID } } var body: some View { Group { if let powerbank { content(for: powerbank) } else { Text("Powerbank not found.") .foregroundColor(.secondary) } } .navigationTitle(powerbank?.name ?? "Powerbank") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Menu { Button("Edit") { isEditing = true } Button("Delete", role: .destructive) { pendingDeletion = true } } label: { Image(systemName: "ellipsis.circle") } .disabled(powerbank == nil) } } .sheet(isPresented: $isEditing) { if let powerbank { PowerbankEditorSheetView(powerbank: powerbank) .environmentObject(appData) } } .confirmationDialog( "Delete \(powerbank?.name ?? "powerbank")?", isPresented: $pendingDeletion, titleVisibility: .visible ) { Button("Delete", role: .destructive) { if let powerbank { _ = appData.deletePowerbank(id: powerbank.id) dismiss() } } Button("Cancel", role: .cancel) {} } message: { Text("This will permanently remove the powerbank and any sessions where it is the subject. Sessions where it was the source will keep their data, with the source link cleared.") } } @ViewBuilder private func content(for powerbank: PowerbankSummary) -> some View { Form { Section(header: Text("Identity")) { row("Name", value: powerbank.name) HStack { Text("QR identifier") Spacer() Text(powerbank.qrIdentifier) .font(.caption.monospaced()) .foregroundColor(.secondary) .textSelection(.enabled) } } Section(header: Text("Battery")) { row("Reporting", value: powerbank.batteryLevelReporting.title) if powerbank.batteryLevelReporting == .bars { row("Bars resolution", value: "\(powerbank.batteryBarsCount)") } if let estimatedCapacityWh = powerbank.estimatedBatteryCapacityWh { row("Capacity (charged)", value: "\(estimatedCapacityWh.format(decimalDigits: 2)) Wh") } if let apparentCapacityWh = powerbank.apparentCapacityWh { row("Apparent capacity (delivered)", value: "\(apparentCapacityWh.format(decimalDigits: 2)) Wh") } if let efficiency = powerbank.sourceEfficiencyFactor { row("Efficiency", value: "\((efficiency * 100).format(decimalDigits: 1))%") } } Section(header: Text("Source statistics")) { if powerbank.sessionsAsSource.isEmpty { Text("No discharge sessions recorded yet.") .font(.caption) .foregroundColor(.secondary) } else { row("Sessions as source", value: "\(powerbank.sessionsAsSource.count)") row("Total Wh delivered", value: "\(powerbank.totalDeliveredEnergyWh.format(decimalDigits: 2)) Wh") if let maxPower = powerbank.sourceMaximumPowerWatts { row("Max power", value: "\(maxPower.format(decimalDigits: 2)) W") } if powerbank.sourceVoltageMaxCurrents.isEmpty == false { VStack(alignment: .leading, spacing: 4) { Text("Voltage profile") .font(.subheadline.weight(.semibold)) ForEach(powerbank.sourceVoltageMaxCurrents.keys.sorted(), id: \.self) { voltage in let maxAmps = powerbank.sourceVoltageMaxCurrents[voltage] ?? 0 HStack { Text(String(format: "%.1f V", voltage)) .font(.caption.monospaced()) Spacer() Text("max \(maxAmps.format(decimalDigits: 2)) A") .font(.caption) .foregroundColor(.secondary) } } } } else if powerbank.sourceObservedVoltageSelections.isEmpty == false { row( "Observed voltages", value: powerbank.sourceObservedVoltageSelections .map { String(format: "%.1fV", $0) } .joined(separator: ", ") ) } } } Section(header: Text("Charging history")) { if powerbank.sessionsAsSubject.isEmpty { Text("Not charged yet.") .font(.caption) .foregroundColor(.secondary) } else { row("Charge sessions", value: "\(powerbank.sessionsAsSubject.count)") row("Total Wh received", value: "\(powerbank.totalReceivedEnergyWh.format(decimalDigits: 2)) Wh") } } if let notes = powerbank.notes, !notes.isEmpty { Section(header: Text("Notes")) { Text(notes) } } } } private func row(_ label: String, value: String) -> some View { HStack { Text(label) Spacer() Text(value) .foregroundColor(.secondary) } } }