GlucoseTargetStepView.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. //
  2. // GlucoseTargetStepView.swift
  3. // Trio
  4. //
  5. // Created by Marvin Polscheit on 19.03.25.
  6. //
  7. import SwiftUI
  8. /// Glucose target step view for setting target glucose range.
  9. struct GlucoseTargetStepView: View {
  10. @State var onboardingData: OnboardingData
  11. @State private var showUnitPicker = false
  12. // Formatter for glucose values
  13. private var numberFormatter: NumberFormatter {
  14. let formatter = NumberFormatter()
  15. formatter.numberStyle = .decimal
  16. formatter.maximumFractionDigits = onboardingData.units == .mmolL ? 1 : 0
  17. return formatter
  18. }
  19. var body: some View {
  20. VStack(alignment: .leading, spacing: 20) {
  21. // Unit selector
  22. HStack {
  23. Text("Blood Glucose Units")
  24. .font(.headline)
  25. Spacer()
  26. Button(action: {
  27. showUnitPicker.toggle()
  28. }) {
  29. HStack {
  30. Text(onboardingData.units == .mgdL ? "mg/dL" : "mmol/L")
  31. Image(systemName: "chevron.down")
  32. }
  33. .padding(.horizontal, 12)
  34. .padding(.vertical, 8)
  35. .background(Color.blue.opacity(0.1))
  36. .cornerRadius(8)
  37. }
  38. .actionSheet(isPresented: $showUnitPicker) {
  39. ActionSheet(
  40. title: Text("Select Blood Glucose Units"),
  41. buttons: [
  42. .default(Text("mg/dL")) {
  43. onboardingData.units = .mgdL
  44. // Adjust values for unit change
  45. if onboardingData.units == .mgdL {
  46. onboardingData.targetLow = max(70, onboardingData.targetLow * 18)
  47. onboardingData.targetHigh = max(120, onboardingData.targetHigh * 18)
  48. onboardingData.isf = max(30, onboardingData.isf * 18)
  49. }
  50. },
  51. .default(Text("mmol/L")) {
  52. onboardingData.units = .mmolL
  53. // Adjust values for unit change
  54. if onboardingData.units == .mmolL {
  55. onboardingData.targetLow = max(3.9, onboardingData.targetLow / 18)
  56. onboardingData.targetHigh = max(6.7, onboardingData.targetHigh / 18)
  57. onboardingData.isf = max(1.7, onboardingData.isf / 18)
  58. }
  59. },
  60. .cancel()
  61. ]
  62. )
  63. }
  64. }
  65. Divider()
  66. // Target glucose range
  67. VStack(alignment: .leading, spacing: 12) {
  68. Text("Target Glucose Range")
  69. .font(.headline)
  70. Text("This range defines your ideal blood glucose values. Trio uses this to calculate insulin doses.")
  71. .font(.subheadline)
  72. .foregroundColor(.secondary)
  73. // Low target
  74. VStack(alignment: .leading) {
  75. Text("Low Target")
  76. .font(.subheadline)
  77. HStack {
  78. Slider(
  79. value: Binding(
  80. get: { Double(truncating: onboardingData.targetLow as NSNumber) },
  81. set: { onboardingData.targetLow = Decimal($0) }
  82. ),
  83. in: onboardingData.units == .mgdL ? 70 ... 120 : 3.9 ... 6.7,
  84. step: onboardingData.units == .mgdL ? 1 : 0.1
  85. )
  86. .accentColor(.green)
  87. Text(
  88. "\(numberFormatter.string(from: onboardingData.targetLow as NSNumber) ?? "--") \(onboardingData.units == .mgdL ? "mg/dL" : "mmol/L")"
  89. )
  90. .frame(width: 80, alignment: .trailing)
  91. }
  92. }
  93. .padding(.vertical, 4)
  94. // High target
  95. VStack(alignment: .leading) {
  96. Text("High Target")
  97. .font(.subheadline)
  98. HStack {
  99. Slider(
  100. value: Binding(
  101. get: { Double(truncating: onboardingData.targetHigh as NSNumber) },
  102. set: { onboardingData.targetHigh = Decimal($0) }
  103. ),
  104. in: onboardingData.units == .mgdL ?
  105. Double(truncating: onboardingData.targetLow as NSNumber) + 10 ... 200 :
  106. Double(truncating: onboardingData.targetLow as NSNumber) + 0.6 ... 11.1,
  107. step: onboardingData.units == .mgdL ? 1 : 0.1
  108. )
  109. .accentColor(.green)
  110. Text(
  111. "\(numberFormatter.string(from: onboardingData.targetHigh as NSNumber) ?? "--") \(onboardingData.units == .mgdL ? "mg/dL" : "mmol/L")"
  112. )
  113. .frame(width: 80, alignment: .trailing)
  114. }
  115. }
  116. .padding(.vertical, 4)
  117. }
  118. Divider()
  119. // Target range visualization
  120. VStack(alignment: .leading, spacing: 8) {
  121. Text("Your Target Range")
  122. .font(.headline)
  123. HStack(spacing: 0) {
  124. // Below range
  125. Rectangle()
  126. .fill(Color.red.opacity(0.3))
  127. .frame(width: 50, height: 30)
  128. .overlay(
  129. Text("Low")
  130. .font(.caption)
  131. .foregroundColor(.red)
  132. )
  133. // Target range
  134. Rectangle()
  135. .fill(Color.green.opacity(0.3))
  136. .frame(width: 100, height: 30)
  137. .overlay(
  138. Text("Target")
  139. .font(.caption)
  140. .foregroundColor(.green)
  141. )
  142. // Above range
  143. Rectangle()
  144. .fill(Color.yellow.opacity(0.3))
  145. .frame(width: 50, height: 30)
  146. .overlay(
  147. Text("High")
  148. .font(.caption)
  149. .foregroundColor(.orange)
  150. )
  151. }
  152. .cornerRadius(8)
  153. // Range values
  154. HStack(spacing: 0) {
  155. Text("\(numberFormatter.string(from: onboardingData.targetLow as NSNumber) ?? "--")")
  156. .font(.caption)
  157. .frame(width: 50, alignment: .center)
  158. Spacer()
  159. .frame(width: 100)
  160. Text("\(numberFormatter.string(from: onboardingData.targetHigh as NSNumber) ?? "--")")
  161. .font(.caption)
  162. .frame(width: 50, alignment: .center)
  163. }
  164. Text("These values reflect your personal target range and can be adjusted at any time in the Settings.")
  165. .font(.caption)
  166. .foregroundColor(.secondary)
  167. .padding(.top, 8)
  168. }
  169. }
  170. .padding()
  171. }
  172. }