Deniz Cengiz 1 год назад
Родитель
Сommit
4f7e591c68

+ 0 - 1
FreeAPS/Sources/Helpers/Calendar+GlucoseStatsChart.swift

@@ -17,4 +17,3 @@ extension Calendar {
         return date(byAdding: .hour, value: hour, to: today) ?? today
     }
 }
-

+ 6 - 6
FreeAPS/Sources/Modules/Stat/StatStateModel.swift

@@ -7,8 +7,8 @@ import Swinject
 extension Stat {
     @Observable final class StateModel: BaseStateModel<Provider> {
         @ObservationIgnored @Injected() var settings: SettingsManager!
-        var highLimit: Decimal = 10 / 0.0555
-        var lowLimit: Decimal = 4 / 0.0555
+        var highLimit: Decimal = 180
+        var lowLimit: Decimal = 70
         var hbA1cDisplayUnit: HbA1cDisplayUnit = .percent
         var timeInRangeChartStyle: TimeInRangeChartStyle = .vertical
         var units: GlucoseUnits = .mgdL
@@ -92,10 +92,10 @@ extension Stat {
 
         enum Duration: String, CaseIterable, Identifiable {
             case Today
-            case Day = "24h"
-            case Week = "7 Days"
-            case Month = "30 Days"
-            case Total = "All"
+            case Day = "D"
+            case Week = "W"
+            case Month = "M"
+            case Total = "3 M."
 
             var id: Self { self }
         }

+ 213 - 106
FreeAPS/Sources/Modules/Stat/View/StatRootView.swift

@@ -16,33 +16,52 @@ extension Stat {
         @Environment(AppState.self) var appState
 
         @State var state = StateModel()
-        @State private var selectedView: ViewType = .statistics
-        @State private var selectedChartType: ChartType = .percentile
-
-        enum ViewType: String, CaseIterable, Identifiable {
-            case statistics = "Time in Range"
-            case tdd = "Total Daily Doses"
-            case loops = "Loop Stats"
-            case meals = "Meal Stats"
+        @State private var selectedView: StatisticViewType = .glucose
+        @State private var selectedGlucoseChartType: GlucoseChartType = .percentile
+        @State private var selectedInsulinChartType: InsulinChartType = .totalDailyDose
+        @State private var selectedLoopingChartType: LoopingChartType = .loopingPerformance
+        @State private var selectedMealChartType: MealChartType = .totalMeals
+
+        enum StatisticViewType: String, CaseIterable, Identifiable {
+            case glucose
+            case insulin
+            case looping
+            case meals
 
             var id: String { rawValue }
             var title: String {
                 switch self {
-                case .statistics: return NSLocalizedString("Time in Range", comment: "Statistics view title")
-                case .tdd: return NSLocalizedString("Total Daily Doses", comment: "TDD view title")
-                case .loops: return NSLocalizedString("Loop Stats", comment: "Loop stats view title")
-                case .meals: return NSLocalizedString("Meal Stats", comment: "Meal stats view title")
+                case .glucose: return "Glucose"
+                case .insulin: return "Insulin"
+                case .looping: return "Looping"
+                case .meals: return "Meals"
                 }
             }
         }
 
-        enum ChartType: String, CaseIterable {
+        enum GlucoseChartType: String, CaseIterable {
             case percentile = "Percentile"
             case stacked = "Distribution"
         }
 
+        enum InsulinChartType: String, CaseIterable {
+            case totalDailyDose = "Total Daily Dose"
+            case bolusDistribution = "Bolus Distribution"
+        }
+
+        enum LoopingChartType: String, CaseIterable {
+            case loopingPerformance = "Looping Performance"
+            case trioUpTime = "Trio Up Time"
+            case cgmConnectionTrace = "CGM Connection Trace"
+        }
+
+        enum MealChartType: String, CaseIterable {
+            case totalMeals = "Total Meals"
+            case mealToHypoHyperDistribution = "Meal to Hypo/Hyper"
+        }
+
         var body: some View {
-            VStack(spacing: Constants.spacing) {
+            VStack {
                 segmentedPicker
 
                 contentView
@@ -63,7 +82,7 @@ extension Stat {
 
         private var segmentedPicker: some View {
             Picker("View", selection: $selectedView) {
-                ForEach(ViewType.allCases) { viewType in
+                ForEach(StatisticViewType.allCases) { viewType in
                     Text(viewType.title).tag(viewType)
                 }
             }
@@ -72,15 +91,20 @@ extension Stat {
         }
 
         @ViewBuilder private var contentView: some View {
-            switch selectedView {
-            case .statistics:
-                statsView()
-            case .tdd:
-                tddView()
-            case .loops:
-                loopsView()
-            case .meals:
-                mealsView()
+            ScrollView {
+                VStack(spacing: Constants.spacing) {
+                    switch selectedView {
+                    case .glucose:
+                        glucoseView()
+                    case .insulin:
+                        insulinView()
+                    case .looping:
+                        loopingView()
+                    case .meals:
+                        mealsView()
+                    }
+                }
+                .padding()
             }
         }
 
@@ -93,27 +117,71 @@ extension Stat {
 
         // MARK: - Stats View
 
-        @ViewBuilder func statsView() -> some View {
-            ScrollView {
-                VStack(spacing: Constants.spacing) {
-                    if state.glucoseFromPersistence.isEmpty {
-                        ContentUnavailableView(
-                            "No Glucose Data",
-                            systemImage: "chart.bar.fill",
-                            description: Text("Glucose statistics will appear here once data is available.")
-                        )
-                    } else {
-                        timeInRangeCard
-                        glucoseStatsCard
+        @ViewBuilder func glucoseView() -> some View {
+            HStack {
+                Text("Chart Type")
+                    .font(.headline)
+
+                Spacer()
+
+                Picker("Glucose Chart Type", selection: $selectedGlucoseChartType) {
+                    ForEach(GlucoseChartType.allCases, id: \.self) { type in
+                        Text(type.rawValue)
                     }
                 }
-                .padding()
+                .pickerStyle(.menu)
+            }.padding(.horizontal)
+
+            Picker("Duration", selection: $state.selectedDuration) {
+                ForEach(StateModel.Duration.allCases, id: \.self) { duration in
+                    Text(duration.rawValue)
+                }
+            }
+            .pickerStyle(.segmented)
+
+            if state.glucoseFromPersistence.isEmpty {
+                ContentUnavailableView(
+                    "No Glucose Data",
+                    systemImage: "chart.bar.fill",
+                    description: Text("Glucose statistics will appear here once data is available.")
+                )
+            } else {
+                timeInRangeCard
+                glucoseStatsCard
             }
         }
 
-        @ViewBuilder func tddView() -> some View {
-            ScrollView {
-                VStack(spacing: Constants.spacing) {
+        @ViewBuilder func insulinView() -> some View {
+            HStack {
+                Text("Chart Type")
+                    .font(.headline)
+
+                Spacer()
+
+                Picker("Insulin Chart Type", selection: $selectedInsulinChartType) {
+                    ForEach(InsulinChartType.allCases, id: \.self) { type in
+                        Text(type.rawValue)
+                    }
+                }.pickerStyle(.menu)
+            }.padding(.horizontal)
+
+            Picker("Duration", selection: $state.selectedDuration) {
+                ForEach(StateModel.Duration.allCases, id: \.self) { duration in
+                    Text(duration.rawValue)
+                }
+            }
+            .pickerStyle(.segmented)
+
+            // TODO: rework TDDChartView and BolusView to respect selectedDays from here and omit datepicker
+            switch selectedInsulinChartType {
+            case .totalDailyDose:
+                if state.dailyTotalDoses.isEmpty || state.currentTDD == 0 {
+                    ContentUnavailableView(
+                        "No TDD Data",
+                        systemImage: "chart.bar.xaxis",
+                        description: Text("Total Daily Doses will appear here once data is available.")
+                    )
+                } else {
                     TDDChartView(
                         state: state,
                         selectedDays: $state.requestedDaysTDD,
@@ -128,62 +196,34 @@ extension Stat {
                     .onChange(of: state.requestedEndDayTDD) {
                         state.updateBolusStats()
                     }
+                }
 
+            case .bolusDistribution:
+                var hasBolusData: Bool {
+                    state.bolusStats.contains { $0.manualBolus > 0 || $0.smb > 0 || $0.external > 0 }
+                }
+
+                if state.bolusStats.isEmpty || !hasBolusData {
+                    ContentUnavailableView(
+                        "No Bolus Data",
+                        systemImage: "cross.vial",
+                        description: Text("Bolus statistics will appear here once data is available.")
+                    )
+                } else {
                     BolusStatsView(
                         bolusStats: state.bolusStats,
                         selectedDays: $state.requestedDaysTDD,
                         selectedEndDate: $state.requestedEndDayTDD
                     )
                 }
-                .padding()
-            }
-        }
-
-        @ViewBuilder func loopsView() -> some View {
-            ScrollView {
-                VStack(spacing: Constants.spacing) {
-                    if state.loopStatRecords.isEmpty {
-                        ContentUnavailableView(
-                            "No Loop Data",
-                            systemImage: "clock.arrow.2.circlepath",
-                            description: Text("Loop statistics will appear here once data is available.")
-                        )
-                    } else {
-                        loopsCard
-                        loopStats
-                    }
-                }
-                .padding()
             }
         }
 
         private var timeInRangeCard: some View {
             StatCard {
                 VStack(spacing: Constants.spacing) {
-                    HStack {
-                        Text("Time in Range")
-                            .font(.headline)
-
-                        Spacer()
-
-                        HStack {
-                            Picker("Duration", selection: $state.selectedDuration) {
-                                ForEach(StateModel.Duration.allCases, id: \.self) { duration in
-                                    Text(duration.rawValue)
-                                }
-                            }
-                            .pickerStyle(.menu)
-
-                            Picker("Chart Type", selection: $selectedChartType) {
-                                ForEach(ChartType.allCases, id: \.self) { type in
-                                    Text(type.rawValue)
-                                }
-                            }
-                            .pickerStyle(.menu)
-                        }
-                    }
-
-                    if selectedChartType == .percentile {
+                    switch selectedGlucoseChartType {
+                    case .percentile:
                         GlucoseAreaChart(
                             glucose: state.glucoseFromPersistence,
                             highLimit: state.highLimit,
@@ -192,7 +232,7 @@ extension Stat {
                             units: state.units,
                             hourlyStats: state.hourlyStats
                         )
-                    } else {
+                    case .stacked:
                         GlucoseStackedAreaChart(
                             glucose: state.glucoseFromPersistence,
                             highLimit: state.highLimit,
@@ -241,23 +281,57 @@ extension Stat {
             }
         }
 
+        @ViewBuilder func loopingView() -> some View {
+            HStack {
+                Text("Chart Type")
+                    .font(.headline)
+
+                Spacer()
+
+                Picker("Looping Chart Type", selection: $selectedLoopingChartType) {
+                    ForEach(LoopingChartType.allCases, id: \.self) { type in
+                        Text(type.rawValue)
+                    }
+                }.pickerStyle(.menu)
+            }.padding(.horizontal)
+
+            Picker("Duration", selection: $state.selectedDuration) {
+                ForEach(StateModel.Duration.allCases, id: \.self) { duration in
+                    Text(duration.rawValue)
+                }
+            }
+            .pickerStyle(.segmented)
+
+            // TODO: ensure looping uses same day selection
+//            Picker("Duration", selection: $state.selectedDurationForLoopStats) {
+//                ForEach(StateModel.Duration.allCases, id: \.self) { duration in
+//                    Text(duration.rawValue)
+//                }
+//            }
+//            .pickerStyle(.menu)
+
+            switch selectedLoopingChartType {
+            case .loopingPerformance:
+                if state.loopStatRecords.isEmpty {
+                    ContentUnavailableView(
+                        "No Loop Data",
+                        systemImage: "clock.arrow.2.circlepath",
+                        description: Text("Loop statistics will appear here once data is available.")
+                    )
+                } else {
+                    loopsCard
+                    loopStats
+                }
+            case .trioUpTime:
+                Text("Not yet implemented")
+            case .cgmConnectionTrace:
+                Text("Not yet implemented")
+            }
+        }
+
         private var loopsCard: some View {
             StatCard {
                 VStack(spacing: Constants.spacing) {
-                    HStack {
-                        Text("Loops")
-                            .font(.headline)
-
-                        Spacer()
-
-                        Picker("Duration", selection: $state.selectedDurationForLoopStats) {
-                            ForEach(StateModel.Duration.allCases, id: \.self) { duration in
-                                Text(duration.rawValue)
-                            }
-                        }
-                        .pickerStyle(.menu)
-                    }
-
                     LoopStatsView(
                         loopStatRecords: state.loopStatRecords,
                         selectedDuration: state.selectedDurationForLoopStats,
@@ -282,21 +356,54 @@ extension Stat {
         }
 
         @ViewBuilder func mealsView() -> some View {
-            ScrollView {
-                VStack(spacing: Constants.spacing) {
-                    Picker("Duration", selection: $state.selectedDurationForMealStats) {
-                        ForEach(StateModel.Duration.allCases, id: \.self) { duration in
-                            Text(duration.rawValue)
-                        }
+            HStack {
+                Text("Chart Type")
+                    .font(.headline)
+
+                Spacer()
+
+                Picker("Meal Chart Type", selection: $selectedMealChartType) {
+                    ForEach(MealChartType.allCases, id: \.self) { type in
+                        Text(type.rawValue)
                     }
-                    .pickerStyle(.menu)
+                }.pickerStyle(.menu)
+            }.padding(.horizontal)
+
+            Picker("Duration", selection: $state.selectedDuration) {
+                ForEach(StateModel.Duration.allCases, id: \.self) { duration in
+                    Text(duration.rawValue)
+                }
+            }
+            .pickerStyle(.segmented)
 
+            // TODO: adjust this so all tabs use the same selected days
+//            Picker("Duration", selection: $state.selectedDurationForMealStats) {
+//                ForEach(StateModel.Duration.allCases, id: \.self) { duration in
+//                    Text(duration.rawValue)
+//                }
+//            }
+//            .pickerStyle(.menu)
+
+            switch selectedMealChartType {
+            case .totalMeals:
+                var hasMealData: Bool {
+                    state.mealStats.contains { $0.carbs > 0 || $0.fat > 0 || $0.protein > 0 }
+                }
+
+                if state.mealStats.isEmpty || !hasMealData {
+                    ContentUnavailableView(
+                        "No Meal Data",
+                        systemImage: "fork.knife",
+                        description: Text("Meal statistics will appear here once data is available.")
+                    )
+                } else {
                     MealStatsView(
                         mealStats: state.mealStats,
                         selectedDuration: state.selectedDurationForMealStats
                     )
                 }
-                .padding()
+            case .mealToHypoHyperDistribution:
+                Text("TODO: Meal to Hypoglycemia/Hyperglycemia Distribution")
             }
         }
     }

+ 36 - 49
FreeAPS/Sources/Modules/Stat/View/ViewElements/BolusStatsView.swift

@@ -6,61 +6,48 @@ struct BolusStatsView: View {
     @Binding var selectedDays: Int
     @Binding var selectedEndDate: Date
 
-    private var hasData: Bool {
-        bolusStats.contains { $0.manualBolus > 0 || $0.smb > 0 || $0.external > 0 }
-    }
-
     var body: some View {
-        if bolusStats.isEmpty || !hasData {
-            ContentUnavailableView(
-                "No Bolus Data",
-                systemImage: "cross.vial",
-                description: Text("Bolus statistics will appear here once data is available.")
-            )
-        } else {
-            StatCard {
-                VStack(alignment: .leading, spacing: 8) {
-                    Text("Bolus Distribution")
-                        .font(.headline)
+        StatCard {
+            VStack(alignment: .leading, spacing: 8) {
+                Text("Bolus Distribution")
+                    .font(.headline)
 
-                    Chart(bolusStats) { stat in
-                        // External Bolus (Bottom)
-                        BarMark(
-                            x: .value("Date", stat.date, unit: .day),
-                            y: .value("Amount", stat.external)
-                        )
-                        .foregroundStyle(by: .value("Type", "External"))
+                Chart(bolusStats) { stat in
+                    // External Bolus (Bottom)
+                    BarMark(
+                        x: .value("Date", stat.date, unit: .day),
+                        y: .value("Amount", stat.external)
+                    )
+                    .foregroundStyle(by: .value("Type", "External"))
 
-                        // SMB (Middle)
-                        BarMark(
-                            x: .value("Date", stat.date, unit: .day),
-                            y: .value("Amount", stat.smb)
-                        )
-                        .foregroundStyle(by: .value("Type", "SMB"))
+                    // SMB (Middle)
+                    BarMark(
+                        x: .value("Date", stat.date, unit: .day),
+                        y: .value("Amount", stat.smb)
+                    )
+                    .foregroundStyle(by: .value("Type", "SMB"))
 
-                        // Manual Bolus (Top)
-                        BarMark(
-                            x: .value("Date", stat.date, unit: .day),
-                            y: .value("Amount", stat.manualBolus)
-                        )
-                        .foregroundStyle(by: .value("Type", "Manual"))
-                    }
-                    .chartForegroundStyleScale([
-                        "Manual": Color.teal,
-                        "SMB": Color.blue,
-                        "External": Color.purple
-                    ])
-                    .chartLegend(position: .top, alignment: .leading, spacing: 12)
-                    .frame(height: 200)
-                    .chartXAxis {
-                        bolusStatsChartXAxisMarks
-                    }
-                    .chartYAxis {
-                        bolusStatsChartYAxisMarks
-                    }
+                    // Manual Bolus (Top)
+                    BarMark(
+                        x: .value("Date", stat.date, unit: .day),
+                        y: .value("Amount", stat.manualBolus)
+                    )
+                    .foregroundStyle(by: .value("Type", "Manual"))
+                }
+                .chartForegroundStyleScale([
+                    "Manual": Color.teal,
+                    "SMB": Color.blue,
+                    "External": Color.purple
+                ])
+                .chartLegend(position: .top, alignment: .leading, spacing: 12)
+                .frame(height: 200)
+                .chartXAxis {
+                    bolusStatsChartXAxisMarks
+                }
+                .chartYAxis {
+                    bolusStatsChartYAxisMarks
                 }
             }
-            .padding()
         }
     }
 

+ 0 - 4
FreeAPS/Sources/Modules/Stat/View/ViewElements/GlucoseAreaChart.swift

@@ -11,10 +11,6 @@ struct GlucoseAreaChart: View {
 
     var body: some View {
         VStack(alignment: .leading, spacing: 8) {
-            Text("Glucose Distribution")
-                .font(.headline)
-                .foregroundStyle(.primary)
-
             Chart {
                 if isTodayOrLast24h {
                     // Single day line chart

+ 0 - 4
FreeAPS/Sources/Modules/Stat/View/ViewElements/GlucoseStackedAreaChart.swift

@@ -11,10 +11,6 @@ struct GlucoseStackedAreaChart: View {
 
     var body: some View {
         VStack(alignment: .leading, spacing: 8) {
-            Text("Glucose Distribution")
-                .font(.headline)
-                .foregroundStyle(.primary)
-
             Chart(glucoseRangeStats) { range in
                 ForEach(range.values, id: \.hour) { value in
                     AreaMark(

+ 42 - 57
FreeAPS/Sources/Modules/Stat/View/ViewElements/MealStatsView.swift

@@ -5,68 +5,53 @@ struct MealStatsView: View {
     let mealStats: [MealStats]
     let selectedDuration: Stat.StateModel.Duration
 
-    private var hasData: Bool {
-        mealStats.contains { $0.carbs > 0 || $0.fat > 0 || $0.protein > 0 }
-    }
-
     var body: some View {
-        ScrollView {
-            if mealStats.isEmpty || !hasData {
-                ContentUnavailableView(
-                    "No Meal Data",
-                    systemImage: "fork.knife",
-                    description: Text("Meal statistics will appear here once data is available.")
-                )
-            } else {
-                StatCard {
-                    VStack(alignment: .leading, spacing: 8) {
-                        Text("Macronutrients")
-                            .font(.headline)
+        StatCard {
+            VStack(alignment: .leading, spacing: 8) {
+                Text("Macronutrients")
+                    .font(.headline)
 
-                        Chart(mealStats) { stat in
-                            // Carbs Bar
-                            BarMark(
-                                x: .value("Date", stat.date, unit: .day),
-                                y: .value("Amount", stat.carbs),
-                                width: .ratio(0.6)
-                            )
-                            .foregroundStyle(Color.orange)
-                            .position(by: .value("Nutrient", "Carbs"))
+                Chart(mealStats) { stat in
+                    // Carbs Bar
+                    BarMark(
+                        x: .value("Date", stat.date, unit: .day),
+                        y: .value("Amount", stat.carbs),
+                        width: .ratio(0.6)
+                    )
+                    .foregroundStyle(Color.orange)
+                    .position(by: .value("Nutrient", "Carbs"))
 
-                            // Fat Bar
-                            BarMark(
-                                x: .value("Date", stat.date, unit: .day),
-                                y: .value("Amount", stat.fat),
-                                width: .ratio(0.6)
-                            )
-                            .foregroundStyle(Color.yellow)
-                            .position(by: .value("Nutrient", "Fat"))
+                    // Fat Bar
+                    BarMark(
+                        x: .value("Date", stat.date, unit: .day),
+                        y: .value("Amount", stat.fat),
+                        width: .ratio(0.6)
+                    )
+                    .foregroundStyle(Color.yellow)
+                    .position(by: .value("Nutrient", "Fat"))
 
-                            // Protein Bar
-                            BarMark(
-                                x: .value("Date", stat.date, unit: .day),
-                                y: .value("Amount", stat.protein),
-                                width: .ratio(0.6)
-                            )
-                            .foregroundStyle(Color.green)
-                            .position(by: .value("Nutrient", "Protein"))
-                        }
-                        .chartForegroundStyleScale([
-                            "Carbs": Color.orange,
-                            "Fat": Color.yellow,
-                            "Protein": Color.green
-                        ])
-                        .chartLegend(position: .top, alignment: .leading, spacing: 12)
-                        .frame(height: 200)
-                        .chartXAxis {
-                            mealChartXAxisMarks
-                        }
-                        .chartYAxis {
-                            mealChartYAxisMarks
-                        }
-                    }
+                    // Protein Bar
+                    BarMark(
+                        x: .value("Date", stat.date, unit: .day),
+                        y: .value("Amount", stat.protein),
+                        width: .ratio(0.6)
+                    )
+                    .foregroundStyle(Color.green)
+                    .position(by: .value("Nutrient", "Protein"))
+                }
+                .chartForegroundStyleScale([
+                    "Carbs": Color.orange,
+                    "Fat": Color.yellow,
+                    "Protein": Color.green
+                ])
+                .chartLegend(position: .top, alignment: .leading, spacing: 12)
+                .frame(height: 200)
+                .chartXAxis {
+                    mealChartXAxisMarks
+                }
+                .chartYAxis {
+                    mealChartYAxisMarks
                 }
-                .padding()
             }
         }
     }

+ 28 - 18
FreeAPS/Sources/Modules/Stat/View/ViewElements/SectorChart.swift

@@ -5,7 +5,7 @@ import SwiftUI
 
 struct SectorChart: View {
     private enum Constants {
-        static let chartHeight: CGFloat = 200
+        static let chartHeight: CGFloat = 160
         static let spacing: CGFloat = 8
         static let labelSpacing: CGFloat = 4
     }
@@ -20,7 +20,7 @@ struct SectorChart: View {
     @Environment(\.colorScheme) var colorScheme
 
     var body: some View {
-        HStack(spacing: 20) {
+        HStack(alignment: .center, spacing: 20) {
             Chart {
                 ForEach(timeInRangeData, id: \.string) { data in
                     SectorMark(
@@ -31,28 +31,28 @@ struct SectorChart: View {
                     .foregroundStyle(data.color.gradient)
                 }
             }
+            .padding(.vertical)
             .frame(height: Constants.chartHeight)
 
             // Legend
             VStack(spacing: Constants.spacing) {
                 ForEach(timeInRangeData, id: \.string) { data in
                     HStack(spacing: Constants.spacing) {
-                        Circle()
-                            .fill(data.color)
-                            .frame(width: 12, height: 12)
+                        Image(systemName: "circle.fill")
+                            .foregroundStyle(data.color)
+                            .font(.caption2)
 
                         Text(data.string)
-                            .font(.subheadline)
+                            .font(.footnote)
 
                         Spacer()
 
                         Text(formatPercentage(data.decimal))
-                            .font(.subheadline)
+                            .font(.footnote)
                             .bold()
                     }
                 }
             }
-            .padding(.top, Constants.spacing)
         }
     }
 
@@ -62,20 +62,30 @@ struct SectorChart: View {
         let total = glucose.count
         guard total > 0 else { return [] }
 
-        let hyperArray = glucose.filter { $0.glucose >= Int(highLimit) }
-        let hyperReadings = hyperArray.count
-        let hyperPercentage = Decimal(hyperReadings) / Decimal(total) * 100
+        let hyperArray = glucose.filter { $0.glucose > Int(highLimit) && $0.glucose <= 250 }
+        let hyperPercentage = Decimal(hyperArray.count) / Decimal(total) * 100
 
-        let hypoArray = glucose.filter { $0.glucose <= Int(lowLimit) }
-        let hypoReadings = hypoArray.count
-        let hypoPercentage = Decimal(hypoReadings) / Decimal(total) * 100
+        let severeHyperArray = glucose.filter { $0.glucose > 250 }
+        let severeHyperPercentage = Decimal(severeHyperArray.count) / Decimal(total) * 100
 
-        let normalPercentage = 100 - (hypoPercentage + hyperPercentage)
+        let hypoArray = glucose.filter { $0.glucose < Int(lowLimit) && $0.glucose > 54 }
+        let hypoPercentage = Decimal(hypoArray.count) / Decimal(total) * 100
+
+        let severeHypoArray = glucose.filter { $0.glucose <= 54 }
+        let severeHypoPercentage = Decimal(severeHypoArray.count) / Decimal(total) * 100
+
+        let normalPercentage = 100 - (hypoPercentage + severeHypoPercentage + severeHyperPercentage + hyperPercentage)
+
+        let timeInTighterRangeArray = glucose.filter { $0.glucose >= Int(lowLimit) && $0.glucose <= 140 }
+        let timeInTighterRangePercentage = Decimal(timeInTighterRangeArray.count) / Decimal(total) * 100
 
         return [
-            (normalPercentage, "In Range", .green),
-            (hyperPercentage, "High", .yellow),
-            (hypoPercentage, "Low", .red)
+            (severeHyperPercentage, "Very High", .orange),
+            (hyperPercentage, "High", .orange.opacity(0.6)),
+            (normalPercentage, "In Range", .green.opacity(0.6)),
+            (timeInTighterRangePercentage, "Tight Range", .green),
+            (hypoPercentage, "Low", .red.opacity(0.6)),
+            (severeHypoPercentage, "Very Low", .red)
         ]
     }
 

+ 5 - 14
FreeAPS/Sources/Modules/Stat/View/ViewElements/TDDChart.swift

@@ -20,19 +20,10 @@ struct TDDChartView: View {
     @Environment(\.colorScheme) var colorScheme
 
     var body: some View {
-        if dailyTotalDoses.isEmpty || state.currentTDD == 0 {
-            ContentUnavailableView(
-                "No TDD Data",
-                systemImage: "chart.bar.xaxis",
-                description: Text("Total Daily Doses will appear here once data is available.")
-            )
-        } else {
-            VStack(spacing: Constants.spacing) {
-                dateSelectionView
-                summaryCardView
-                chartCard
-            }
-            .padding()
+        VStack(spacing: Constants.spacing) {
+            dateSelectionView
+            summaryCardView
+            chartCard
         }
     }
 
@@ -54,7 +45,7 @@ struct TDDChartView: View {
                     Text("\(days) days").tag(days)
                 }
             }
-            .pickerStyle(MenuPickerStyle())
+            .pickerStyle(.segmented)
         }
     }