Showing 1 changed files with 81 additions and 19 deletions
+81 -19
USB Meter/Views/ChargedDevices/Details/ChargedDeviceDetailView.swift
@@ -145,8 +145,9 @@ struct ChargedDeviceDetailView: View {
145 145
             activeSessionSummaryCard(activeSession, chargedDevice: chargedDevice)
146 146
         }
147 147
 
148
-        if !closedSessions(for: chargedDevice).isEmpty {
149
-            sessionHistorySummaryCard(chargedDevice)
148
+        let sessions = closedSessions(for: chargedDevice)
149
+        if !sessions.isEmpty {
150
+            sessionListCard(sessions, chargedDevice: chargedDevice)
150 151
         } else if chargedDevice.activeSession == nil {
151 152
             emptyStateCard(
152 153
                 title: "No Sessions",
@@ -615,31 +616,92 @@ struct ChargedDeviceDetailView: View {
615 616
         .meterCard(tint: .blue, fillOpacity: 0.14, strokeOpacity: 0.20, cornerRadius: 18)
616 617
     }
617 618
 
618
-    private func sessionHistorySummaryCard(_ chargedDevice: ChargedDeviceSummary) -> some View {
619
-        let sessions = closedSessions(for: chargedDevice)
620
-        let latestSession = sessions.first
619
+    private func sessionListCard(
620
+        _ sessions: [ChargeSessionSummary],
621
+        chargedDevice: ChargedDeviceSummary
622
+    ) -> some View {
621 623
         let totalEnergyWh = sessions.reduce(0) { $0 + $1.effectiveOrMeasuredEnergyWh }
624
+        let completedCount = sessions.filter { $0.status == .completed }.count
622 625
 
623
-        return MeterInfoCardView(title: "Session History", tint: .teal) {
624
-            MeterInfoRowView(label: "Closed Sessions", value: "\(sessions.count)")
625
-            MeterInfoRowView(label: "Total Energy", value: "\(totalEnergyWh.format(decimalDigits: 2)) Wh")
626
-            if let latestSession {
627
-                MeterInfoRowView(label: "Latest", value: latestSession.startedAt.format())
626
+        return VStack(alignment: .leading, spacing: 14) {
627
+            MeterInfoCardView(title: "Closed Sessions", tint: .teal) {
628
+                MeterInfoRowView(label: "Sessions", value: "\(sessions.count)")
629
+                MeterInfoRowView(label: "Completed", value: "\(completedCount)")
630
+                MeterInfoRowView(label: "Total Energy", value: "\(totalEnergyWh.format(decimalDigits: 2)) Wh")
628 631
             }
629 632
 
630
-            NavigationLink(
631
-                destination: ChargedDeviceSessionsView(chargedDeviceID: chargedDevice.id)
632
-            ) {
633
-                Label("Manage Sessions", systemImage: "clock.arrow.trianglehead.counterclockwise.rotate.90")
634
-                    .font(.subheadline.weight(.semibold))
635
-                    .frame(maxWidth: .infinity)
636
-                    .padding(.vertical, 10)
637
-                    .meterCard(tint: .teal, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14)
633
+            VStack(spacing: 10) {
634
+                ForEach(sessions.sorted { $0.startedAt > $1.startedAt }, id: \.id) { session in
635
+                    sessionListItem(session, chargedDevice: chargedDevice)
636
+                }
638 637
             }
639
-            .buttonStyle(.plain)
640 638
         }
641 639
     }
642 640
 
641
+    private func sessionListItem(
642
+        _ session: ChargeSessionSummary,
643
+        chargedDevice: ChargedDeviceSummary
644
+    ) -> some View {
645
+        let sessionTint = statusTint(for: session)
646
+
647
+        return NavigationLink(
648
+            destination: ChargeSessionDetailView(
649
+                chargedDeviceID: chargedDevice.id,
650
+                sessionID: session.id
651
+            )
652
+        ) {
653
+            VStack(alignment: .leading, spacing: 10) {
654
+                HStack(alignment: .firstTextBaseline, spacing: 10) {
655
+                    VStack(alignment: .leading, spacing: 2) {
656
+                        Text(session.startedAt.format())
657
+                            .font(.subheadline.weight(.semibold))
658
+                        Text(session.status.title)
659
+                            .font(.caption2)
660
+                            .foregroundColor(sessionTint)
661
+                    }
662
+
663
+                    Spacer()
664
+
665
+                    VStack(alignment: .trailing, spacing: 2) {
666
+                        Text("\(session.effectiveOrMeasuredEnergyWh.format(decimalDigits: 2)) Wh")
667
+                            .font(.subheadline.weight(.semibold))
668
+                            .foregroundColor(.primary)
669
+                        Text(sessionDurationText(session))
670
+                            .font(.caption)
671
+                            .foregroundColor(.secondary)
672
+                    }
673
+                }
674
+
675
+                Divider()
676
+
677
+                HStack(spacing: 8) {
678
+                    if let batteryDelta = session.batteryDeltaPercent {
679
+                        Label("\(batteryDelta >= 0 ? "+" : "")\(Int(batteryDelta.rounded()))% charged", systemImage: "battery.100percent")
680
+                            .font(.caption2)
681
+                            .foregroundColor(.secondary)
682
+                    }
683
+
684
+                    if let capacityWh = session.capacityEstimateWh {
685
+                        Text("est. \(capacityWh.format(decimalDigits: 1)) Wh")
686
+                            .font(.caption2)
687
+                            .foregroundColor(.secondary)
688
+                    }
689
+
690
+                    Spacer()
691
+
692
+                    if !session.displayedAggregatedSamples.isEmpty {
693
+                        Label("\(session.displayedAggregatedSamples.count) points", systemImage: "chart.xyaxis.line")
694
+                            .font(.caption2)
695
+                            .foregroundColor(.secondary)
696
+                    }
697
+                }
698
+            }
699
+            .padding(12)
700
+            .meterCard(tint: sessionTint, fillOpacity: 0.08, strokeOpacity: 0.14, cornerRadius: 14)
701
+        }
702
+        .buttonStyle(.plain)
703
+    }
704
+
643 705
     private func closedSessions(for chargedDevice: ChargedDeviceSummary) -> [ChargeSessionSummary] {
644 706
         chargedDevice.sessions.filter { !$0.status.isOpen }
645 707
     }