Showing 1 changed files with 128 additions and 102 deletions
+128 -102
USB Meter/Views/ChargedDevices/Sessions/ChargeSessionDetailView.swift
@@ -197,8 +197,6 @@ struct ChargeSessionDetailView: View {
197 197
                     }
198 198
                 } else {
199 199
                     overviewCard(session, chargedDevice: chargedDevice)
200
-                    energyCard(session, chargedDevice: chargedDevice)
201
-                    observedMetricsCard(session, chargedDevice: chargedDevice)
202 200
                     batteryCard(session, chargedDevice: chargedDevice)
203 201
 
204 202
                     if shouldShowSessionChart(session) {
@@ -396,6 +394,55 @@ struct ChargeSessionDetailView: View {
396 394
                         }
397 395
                     }
398 396
                 }
397
+
398
+                if session.minimumObservedCurrentAmps != nil
399
+                    || session.maximumObservedCurrentAmps != nil
400
+                    || session.maximumObservedPowerWatts != nil
401
+                    || session.maximumObservedVoltageVolts != nil
402
+                    || (chargedDevice.isCharger && session.selectedSourceVoltageVolts != nil)
403
+                    || session.completionCurrentAmps != nil
404
+                    || session.selectedDataGroup != nil {
405
+
406
+                    Divider()
407
+
408
+                    if session.minimumObservedCurrentAmps != nil || session.maximumObservedCurrentAmps != nil {
409
+                        HStack(alignment: .top, spacing: 12) {
410
+                            if let v = session.minimumObservedCurrentAmps {
411
+                                overviewStatCell(label: "Min Current", value: "\(v.format(decimalDigits: 2)) A")
412
+                            }
413
+                            if let v = session.maximumObservedCurrentAmps {
414
+                                overviewStatCell(label: "Max Current", value: "\(v.format(decimalDigits: 2)) A")
415
+                            }
416
+                        }
417
+                    }
418
+
419
+                    if session.maximumObservedPowerWatts != nil || session.maximumObservedVoltageVolts != nil {
420
+                        HStack(alignment: .top, spacing: 12) {
421
+                            if let v = session.maximumObservedPowerWatts {
422
+                                overviewStatCell(label: "Max Power", value: "\(v.format(decimalDigits: 2)) W")
423
+                            }
424
+                            if let v = session.maximumObservedVoltageVolts {
425
+                                overviewStatCell(label: "Max Voltage", value: "\(v.format(decimalDigits: 2)) V")
426
+                            }
427
+                        }
428
+                    }
429
+
430
+                    if session.completionCurrentAmps != nil
431
+                        || (chargedDevice.isCharger && session.selectedSourceVoltageVolts != nil) {
432
+                        HStack(alignment: .top, spacing: 12) {
433
+                            if let v = session.completionCurrentAmps {
434
+                                overviewStatCell(label: "Completion Current", value: "\(v.format(decimalDigits: 2)) A")
435
+                            }
436
+                            if chargedDevice.isCharger, let v = session.selectedSourceVoltageVolts {
437
+                                overviewStatCell(label: "Selected Voltage", value: "\(v.format(decimalDigits: 2)) V")
438
+                            }
439
+                        }
440
+                    }
441
+
442
+                    if let dg = session.selectedDataGroup {
443
+                        MeterInfoRowView(label: "Data Group", value: "\(dg)")
444
+                    }
445
+                }
399 446
             }
400 447
         }
401 448
     }
@@ -412,70 +459,6 @@ struct ChargeSessionDetailView: View {
412 459
         .frame(maxWidth: .infinity, alignment: .leading)
413 460
     }
414 461
 
415
-    private func energyCard(
416
-        _ session: ChargeSessionSummary,
417
-        chargedDevice: ChargedDeviceSummary
418
-    ) -> some View {
419
-        let displayedEnergyWh = displayedSessionEnergyWh(for: session)
420
-
421
-        return MeterInfoCardView(title: "Energy", tint: .teal, isCollapsible: true) {
422
-            MeterInfoRowView(label: "Battery Energy", value: "\(displayedEnergyWh.format(decimalDigits: 3)) Wh")
423
-            if abs(displayedEnergyWh - session.measuredEnergyWh) > 0.01 {
424
-                MeterInfoRowView(label: "Measured Energy", value: "\(session.measuredEnergyWh.format(decimalDigits: 3)) Wh")
425
-            }
426
-            if let effectiveBatteryEnergyWh = session.effectiveBatteryEnergyWh,
427
-               abs(effectiveBatteryEnergyWh - session.measuredEnergyWh) > 0.01 {
428
-                MeterInfoRowView(label: "Effective Battery Energy", value: "\(effectiveBatteryEnergyWh.format(decimalDigits: 3)) Wh")
429
-            }
430
-            if let capacityEstimateWh = session.capacityEstimateWh {
431
-                MeterInfoRowView(label: "Capacity Estimate", value: "\(capacityEstimateWh.format(decimalDigits: 2)) Wh")
432
-            }
433
-            if let chargerID = session.chargerID,
434
-               let charger = appData.chargedDeviceSummary(id: chargerID) {
435
-                MeterInfoRowView(label: chargedDevice.hasMultipleChargingTransports ? "Wireless Charger" : "Charger", value: charger.name)
436
-            }
437
-            if let wirelessSessionHint = wirelessSessionHint(for: session) {
438
-                Text(wirelessSessionHint)
439
-                    .font(.caption2)
440
-                    .foregroundColor(session.shouldWarnAboutLowWirelessEfficiency ? .orange : .secondary)
441
-            }
442
-            if let sessionWarning = sessionWarning(for: session) {
443
-                Label(sessionWarning, systemImage: "exclamationmark.triangle")
444
-                    .font(.caption2)
445
-                    .foregroundColor(.orange)
446
-            }
447
-        }
448
-    }
449
-
450
-    private func observedMetricsCard(
451
-        _ session: ChargeSessionSummary,
452
-        chargedDevice: ChargedDeviceSummary
453
-    ) -> some View {
454
-        MeterInfoCardView(title: "Observed Metrics", tint: .blue, isCollapsible: true, initiallyExpanded: false) {
455
-            if let minimumObservedCurrentAmps = session.minimumObservedCurrentAmps {
456
-                MeterInfoRowView(label: "Min Current", value: "\(minimumObservedCurrentAmps.format(decimalDigits: 2)) A")
457
-            }
458
-            if let maximumObservedCurrentAmps = session.maximumObservedCurrentAmps {
459
-                MeterInfoRowView(label: "Max Current", value: "\(maximumObservedCurrentAmps.format(decimalDigits: 2)) A")
460
-            }
461
-            if let maximumObservedPowerWatts = session.maximumObservedPowerWatts {
462
-                MeterInfoRowView(label: "Max Power", value: "\(maximumObservedPowerWatts.format(decimalDigits: 2)) W")
463
-            }
464
-            if let maximumObservedVoltageVolts = session.maximumObservedVoltageVolts {
465
-                MeterInfoRowView(label: "Max Voltage", value: "\(maximumObservedVoltageVolts.format(decimalDigits: 2)) V")
466
-            }
467
-            if chargedDevice.isCharger, let selectedSourceVoltageVolts = session.selectedSourceVoltageVolts {
468
-                MeterInfoRowView(label: "Selected Voltage", value: "\(selectedSourceVoltageVolts.format(decimalDigits: 2)) V")
469
-            }
470
-            if let completionCurrentAmps = session.completionCurrentAmps {
471
-                MeterInfoRowView(label: "Completion Current", value: "\(completionCurrentAmps.format(decimalDigits: 2)) A")
472
-            }
473
-            if session.selectedDataGroup != nil {
474
-                MeterInfoRowView(label: "Data Group", value: "\(session.selectedDataGroup ?? 0)")
475
-            }
476
-        }
477
-    }
478
-
479 462
     private func batteryCard(
480 463
         _ session: ChargeSessionSummary,
481 464
         chargedDevice: ChargedDeviceSummary
@@ -487,44 +470,87 @@ struct ChargeSessionDetailView: View {
487 470
         )
488 471
 
489 472
         return MeterInfoCardView(title: "Battery", tint: .orange, isCollapsible: true) {
490
-            if let startBatteryPercent = session.startBatteryPercent {
491
-                MeterInfoRowView(label: "Start Battery", value: "\(startBatteryPercent.format(decimalDigits: 0))%")
492
-            }
493
-            if let endBatteryPercent = session.endBatteryPercent {
494
-                MeterInfoRowView(label: "End Battery", value: "\(endBatteryPercent.format(decimalDigits: 0))%")
495
-            }
496
-            if let batteryDeltaPercent = session.batteryDeltaPercent {
497
-                MeterInfoRowView(label: "Battery Delta", value: "\(batteryDeltaPercent.format(decimalDigits: 0))%")
498
-            }
499
-            if let targetBatteryPercent = session.targetBatteryPercent {
500
-                MeterInfoRowView(label: "Target Notification", value: "\(targetBatteryPercent.format(decimalDigits: 0))%")
501
-            }
502
-            if let batteryPrediction {
503
-                MeterInfoRowView(
504
-                    label: "Predicted Battery",
505
-                    value: "\(batteryPrediction.predictedPercent.format(decimalDigits: 0))%"
506
-                )
507
-                Text(
508
-                    "Anchored to \(batteryPrediction.anchorDescription) at \(batteryPrediction.anchorPercent.format(decimalDigits: 0))% using \(batteryPrediction.estimatedCapacityWh.format(decimalDigits: 2)) Wh estimated capacity."
509
-                )
510
-                .font(.caption2)
511
-                .foregroundColor(.secondary)
512
-            }
473
+            VStack(alignment: .leading, spacing: 10) {
513 474
 
514
-            BatteryCheckpointSectionView(
515
-                sessionID: session.id,
516
-                checkpoints: session.checkpoints,
517
-                message: session.status.isOpen
518
-                    ? "The checkpoint is stored on the active charge session and is used for capacity estimation and charge-level prediction."
519
-                    : "These checkpoints were saved with this closed session and feed capacity estimation and charge-level prediction.",
520
-                canAddCheckpoint: hasMonitoringControls && appData.canAddBatteryCheckpoint(to: session.id),
521
-                canDeleteCheckpoint: hasMonitoringControls || session.status.isOpen == false,
522
-                requirementMessage: session.status.isOpen ? appData.batteryCheckpointCaptureRequirementMessage(for: session.id) : nil,
523
-                effectiveEnergyWhOverride: hasMonitoringControls ? displayedEnergyWh : nil,
524
-                onDelete: { checkpoint in
525
-                    pendingCheckpointDeletion = checkpoint
475
+                // Energy
476
+                HStack(alignment: .top, spacing: 12) {
477
+                    overviewStatCell(label: "Battery Energy", value: "\(displayedEnergyWh.format(decimalDigits: 3)) Wh")
478
+                    if let capacityEstimateWh = session.capacityEstimateWh {
479
+                        overviewStatCell(label: "Capacity Estimate", value: "\(capacityEstimateWh.format(decimalDigits: 2)) Wh")
480
+                    }
526 481
                 }
527
-            )
482
+                if abs(displayedEnergyWh - session.measuredEnergyWh) > 0.01 {
483
+                    MeterInfoRowView(label: "Measured Energy", value: "\(session.measuredEnergyWh.format(decimalDigits: 3)) Wh")
484
+                }
485
+                if let effectiveBatteryEnergyWh = session.effectiveBatteryEnergyWh,
486
+                   abs(effectiveBatteryEnergyWh - session.measuredEnergyWh) > 0.01 {
487
+                    MeterInfoRowView(label: "Effective Battery Energy", value: "\(effectiveBatteryEnergyWh.format(decimalDigits: 3)) Wh")
488
+                }
489
+                if let chargerID = session.chargerID,
490
+                   let charger = appData.chargedDeviceSummary(id: chargerID) {
491
+                    MeterInfoRowView(label: chargedDevice.hasMultipleChargingTransports ? "Wireless Charger" : "Charger", value: charger.name)
492
+                }
493
+                if let wirelessSessionHint = wirelessSessionHint(for: session) {
494
+                    Text(wirelessSessionHint)
495
+                        .font(.caption2)
496
+                        .foregroundColor(session.shouldWarnAboutLowWirelessEfficiency ? .orange : .secondary)
497
+                }
498
+                if let sessionWarning = sessionWarning(for: session) {
499
+                    Label(sessionWarning, systemImage: "exclamationmark.triangle")
500
+                        .font(.caption2)
501
+                        .foregroundColor(.orange)
502
+                }
503
+
504
+                // Battery percentages
505
+                if session.startBatteryPercent != nil || session.endBatteryPercent != nil {
506
+                    Divider()
507
+                    HStack(alignment: .top, spacing: 12) {
508
+                        if let v = session.startBatteryPercent {
509
+                            overviewStatCell(label: "Start Battery", value: "\(v.format(decimalDigits: 0))%")
510
+                        }
511
+                        if let v = session.endBatteryPercent {
512
+                            overviewStatCell(label: "End Battery", value: "\(v.format(decimalDigits: 0))%")
513
+                        }
514
+                    }
515
+                    if session.batteryDeltaPercent != nil || session.targetBatteryPercent != nil {
516
+                        HStack(alignment: .top, spacing: 12) {
517
+                            if let v = session.batteryDeltaPercent {
518
+                                overviewStatCell(label: "Battery Delta", value: "\(v.format(decimalDigits: 0))%")
519
+                            }
520
+                            if let v = session.targetBatteryPercent {
521
+                                overviewStatCell(label: "Target", value: "\(v.format(decimalDigits: 0))%")
522
+                            }
523
+                        }
524
+                    }
525
+                    if let batteryPrediction {
526
+                        HStack(alignment: .top, spacing: 12) {
527
+                            overviewStatCell(label: "Predicted Battery", value: "\(batteryPrediction.predictedPercent.format(decimalDigits: 0))%")
528
+                        }
529
+                        Text(
530
+                            "Anchored to \(batteryPrediction.anchorDescription) at \(batteryPrediction.anchorPercent.format(decimalDigits: 0))% using \(batteryPrediction.estimatedCapacityWh.format(decimalDigits: 2)) Wh estimated capacity."
531
+                        )
532
+                        .font(.caption2)
533
+                        .foregroundColor(.secondary)
534
+                    }
535
+                }
536
+
537
+                // Checkpoints
538
+                Divider()
539
+                BatteryCheckpointSectionView(
540
+                    sessionID: session.id,
541
+                    checkpoints: session.checkpoints,
542
+                    message: session.status.isOpen
543
+                        ? "The checkpoint is stored on the active charge session and is used for capacity estimation and charge-level prediction."
544
+                        : "These checkpoints were saved with this closed session and feed capacity estimation and charge-level prediction.",
545
+                    canAddCheckpoint: hasMonitoringControls && appData.canAddBatteryCheckpoint(to: session.id),
546
+                    canDeleteCheckpoint: hasMonitoringControls || session.status.isOpen == false,
547
+                    requirementMessage: session.status.isOpen ? appData.batteryCheckpointCaptureRequirementMessage(for: session.id) : nil,
548
+                    effectiveEnergyWhOverride: hasMonitoringControls ? displayedEnergyWh : nil,
549
+                    onDelete: { checkpoint in
550
+                        pendingCheckpointDeletion = checkpoint
551
+                    }
552
+                )
553
+            }
528 554
         }
529 555
     }
530 556