/ BalanceKit / OnboardingView.swift
OnboardingView.swift
  1  //
  2  //  OnboardingView.swift
  3  //  BalanceKit
  4  //
  5  //  Created by Alexander Kunau on 30.10.24.
  6  //
  7  
  8  import SwiftUI
  9  import HealthKit
 10  
 11  struct OnboardingView: View {
 12      @ObservedObject var userProfile: UserProfile
 13      @ObservedObject var dataManager: FoodDataManager
 14      @State private var currentStep = 0
 15      @Environment(\.dismiss) private var dismiss
 16      @State private var shouldDismiss = false
 17      
 18      var body: some View {
 19          VStack {
 20              // Progress bar
 21              HStack(spacing: 4) {
 22                  ForEach(0..<4) { step in
 23                      Rectangle()
 24                          .frame(height: 4)
 25                          .foregroundColor(step <= currentStep ? .blue : .gray.opacity(0.3))
 26                  }
 27              }
 28              .padding(.horizontal)
 29              .padding(.top)
 30              
 31              ScrollView {
 32                  VStack(alignment: .leading, spacing: 20) {
 33                      // Step 1: Willkommen
 34                      if currentStep == 0 {
 35                          welcomeStep
 36                      }
 37                      // Step 2: Persönliche Daten
 38                      else if currentStep == 1 {
 39                          personalDataStep
 40                      }
 41                      // Step 3: Aktivität & Ziel
 42                      else if currentStep == 2 {
 43                          activityAndGoalStep
 44                      }
 45                      // Step 4: Zusammenfassung
 46                      else if currentStep == 3 {
 47                          summaryStep
 48                      }
 49                  }
 50                  .padding()
 51                  .frame(maxWidth: .infinity)
 52              }
 53              
 54              // Navigation buttons
 55              HStack {
 56                  if currentStep > 0 {
 57                      Button(action: {
 58                          withAnimation {
 59                              currentStep -= 1
 60                          }
 61                      }) {
 62                          HStack {
 63                              Image(systemName: "chevron.left")
 64                              Text("Zurück")
 65                          }
 66                          .foregroundColor(.blue)
 67                          .padding()
 68                      }
 69                  }
 70                  
 71                  Spacer()
 72                  
 73                  Button(action: {
 74                      withAnimation {
 75                          if currentStep < 3 {
 76                              currentStep += 1
 77                          } else {
 78                              completeOnboarding()
 79                          }
 80                      }
 81                  }) {
 82                      HStack {
 83                          Text(currentStep == 3 ? "Fertig stellen" : "Weiter")
 84                          Image(systemName: "chevron.right")
 85                      }
 86                      .foregroundColor(.white)
 87                      .padding()
 88                      .background(Color.blue)
 89                      .cornerRadius(10)
 90                  }
 91              }
 92              .padding()
 93          }
 94          .navigationBarBackButtonHidden(true)
 95          .onChange(of: shouldDismiss) { oldValue, newValue in
 96              if newValue {
 97                  // Setze eine kurze Verzögerung, um sicherzustellen, dass alle Daten gespeichert sind
 98                  DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
 99                      dismiss()
100                  }
101              }
102          }
103      }
104      
105      // Step 1: Willkommensbildschirm
106      var welcomeStep: some View {
107          VStack(alignment: .leading, spacing: 20) {
108              Text("Willkommen bei Foodi")
109                  .font(.largeTitle)
110                  .fontWeight(.bold)
111              
112              Text("Wir möchten dir helfen, deine Ernährungsziele zu erreichen. Dafür brauchen wir ein paar Informationen über dich.")
113                  .foregroundColor(.secondary)
114              
115              Image(systemName: "fork.knife.circle.fill")
116                  .resizable()
117                  .aspectRatio(contentMode: .fit)
118                  .frame(width: 100, height: 100)
119                  .foregroundColor(.blue)
120                  .frame(maxWidth: .infinity)
121                  .padding(.vertical, 20)
122              
123              Text("In den nächsten Schritten kannst du deine persönlichen Daten angeben, damit wir deinen täglichen Kalorienbedarf und empfohlene Makronährstoffe berechnen können.")
124                  .foregroundColor(.secondary)
125          }
126      }
127      
128      // Step 2: Persönliche Daten
129      var personalDataStep: some View {
130          VStack(alignment: .leading, spacing: 20) {
131              Text("Persönliche Daten")
132                  .font(.title)
133                  .fontWeight(.bold)
134              
135              VStack(alignment: .leading) {
136                  Text("Alter").fontWeight(.medium)
137                  Stepper("\(userProfile.age) Jahre", value: $userProfile.age, in: 16...100, step: 1)
138              }
139              .padding(.vertical, 5)
140              
141              VStack(alignment: .leading) {
142                  Text("Geschlecht").fontWeight(.medium)
143                  Picker("Geschlecht", selection: $userProfile.gender) {
144                      ForEach(Gender.allCases) { gender in
145                          Text(gender.rawValue).tag(gender)
146                      }
147                  }
148                  .pickerStyle(.segmented)
149              }
150              .padding(.vertical, 5)
151              
152              VStack(alignment: .leading) {
153                  Text("Größe (cm)").fontWeight(.medium)
154                  HStack {
155                      Slider(value: $userProfile.height, in: 140...220, step: 1)
156                      Text("\(Int(userProfile.height)) cm")
157                          .frame(width: 70, alignment: .trailing)
158                  }
159              }
160              .padding(.vertical, 5)
161              
162              VStack(alignment: .leading) {
163                  Text("Aktuelles Gewicht (kg)").fontWeight(.medium)
164                  HStack {
165                      Slider(value: $userProfile.weight, in: 40...180, step: 0.5)
166                      Text(String(format: "%.1f kg", userProfile.weight))
167                          .frame(width: 70, alignment: .trailing)
168                  }
169              }
170              .padding(.vertical, 5)
171              
172              VStack(alignment: .leading) {
173                  Text("Zielgewicht (kg)").fontWeight(.medium)
174                  HStack {
175                      Slider(value: $userProfile.targetWeight, in: 40...180, step: 0.5)
176                      Text(String(format: "%.1f kg", userProfile.targetWeight))
177                          .frame(width: 70, alignment: .trailing)
178                  }
179              }
180              .padding(.vertical, 5)
181          }
182      }
183      
184      // Step 3: Aktivität & Ziel
185      var activityAndGoalStep: some View {
186          VStack(alignment: .leading, spacing: 20) {
187              Text("Aktivität & Ziel")
188                  .font(.title)
189                  .fontWeight(.bold)
190              
191              VStack(alignment: .leading) {
192                  Text("Wie aktiv bist du?").fontWeight(.medium)
193                  Picker("Aktivitätsniveau", selection: $userProfile.activityLevel) {
194                      ForEach(ActivityLevel.allCases) { level in
195                          Text(level.rawValue).tag(level)
196                      }
197                  }
198                  .pickerStyle(.navigationLink)
199              }
200              .padding(.vertical, 5)
201              
202              VStack(alignment: .leading) {
203                  Text("Was ist dein Ziel?").fontWeight(.medium)
204                  Picker("Ziel", selection: $userProfile.weightGoal) {
205                      ForEach(WeightGoal.allCases) { goal in
206                          Text(goal.rawValue).tag(goal)
207                      }
208                  }
209                  .pickerStyle(.segmented)
210              }
211              .padding(.vertical, 5)
212          }
213      }
214      
215      // Step 4: Zusammenfassung
216      var summaryStep: some View {
217          VStack(alignment: .leading, spacing: 20) {
218              Text("Deine persönlichen Empfehlungen")
219                  .font(.title)
220                  .fontWeight(.bold)
221              
222              VStack(alignment: .leading, spacing: 5) {
223                  Text("Täglicher Kalorienbedarf")
224                      .fontWeight(.medium)
225                  Text("\(userProfile.dailyCalorieNeeds) kcal")
226                      .font(.title)
227                      .foregroundColor(.green)
228              }
229              .padding(.vertical, 5)
230              
231              let macros = userProfile.recommendedMacros
232              VStack(alignment: .leading, spacing: 15) {
233                  Text("Empfohlene Makronährstoffe")
234                      .fontWeight(.medium)
235                  
236                  HStack {
237                      MacroCard(
238                          title: "Protein", 
239                          value: Int(macros.protein), 
240                          unit: "g",
241                          color: .blue
242                      )
243                      
244                      MacroCard(
245                          title: "Kohlenhydrate", 
246                          value: Int(macros.carbs), 
247                          unit: "g",
248                          color: .orange
249                      )
250                      
251                      MacroCard(
252                          title: "Fett", 
253                          value: Int(macros.fat), 
254                          unit: "g",
255                          color: .purple
256                      )
257                  }
258              }
259              .padding(.vertical, 5)
260              
261              Text("Diese Werte basieren auf deinen persönlichen Daten und werden verwendet, um deine Zielwerte in der App zu setzen.")
262                  .foregroundColor(.secondary)
263                  .font(.footnote)
264                  .padding(.vertical, 5)
265              
266              Divider()
267                  .padding(.vertical, 10)
268              
269              VStack(alignment: .leading, spacing: 10) {
270                  Text("Alternative: Custom Werte")
271                      .fontWeight(.semibold)
272                  Text("Du kannst diese Empfehlungen übernehmen oder später in den Einstellungen unter 'Zielwerte' eigene Werte festlegen.")
273                      .font(.footnote)
274                      .foregroundColor(.secondary)
275              }
276          }
277      }
278      
279      private func completeOnboarding() {
280          // Setze die berechneten Zielwerte in den DataManager
281          dataManager.dailyCalorieGoal = userProfile.dailyCalorieNeeds
282          
283          let macros = userProfile.recommendedMacros
284          dataManager.dailyProteinGoal = macros.protein
285          dataManager.dailyCarbsGoal = macros.carbs
286          dataManager.dailyFatGoal = macros.fat
287          
288          // Stelle sicher, dass der HealthManager initialisiert ist (falls auf echtem Gerät)
289          #if !targetEnvironment(simulator)
290          if HKHealthStore.isHealthDataAvailable() {
291              print("DEBUG: Onboarding - HealthKit ist verfügbar, initialisiere HealthManager")
292              if dataManager.healthManager == nil {
293                  dataManager.healthManager = HealthManager()
294              } else {
295                  print("DEBUG: Onboarding - HealthManager ist bereits initialisiert")
296                  // Aktualisiere Daten
297                  dataManager.healthManager?.requestAuthorization()
298              }
299          } else {
300              print("DEBUG: Onboarding - HealthKit ist nicht verfügbar")
301          }
302          #endif
303          
304          // Markiere Onboarding als abgeschlossen
305          userProfile.isOnboardingCompleted = true
306          userProfile.save()
307          
308          // Schließe die Onboarding-Ansicht
309          shouldDismiss = true
310      }
311  }
312  
313  // Eine Hilfsansicht für die Makronährstoffkarten
314  struct MacroCard: View {
315      var title: String
316      var value: Int
317      var unit: String
318      var color: Color
319      
320      var body: some View {
321          VStack {
322              Text(title)
323                  .font(.caption)
324                  .foregroundColor(.secondary)
325                  .lineLimit(1)
326                  .minimumScaleFactor(0.7)
327              
328              Text("\(value)")
329                  .font(.headline)
330                  .foregroundColor(color)
331              
332              Text(unit)
333                  .font(.caption)
334                  .foregroundColor(.secondary)
335          }
336          .frame(maxWidth: .infinity)
337          .padding()
338          .background(Color.gray.opacity(0.1))
339          .cornerRadius(10)
340      }
341  }
342  
343  #Preview {
344      OnboardingView(userProfile: UserProfile(), dataManager: FoodDataManager())
345  }