/ BalanceKit / ContentView.swift
ContentView.swift
  1  //
  2  //  ContentView.swift
  3  //  BalanceKit
  4  //
  5  //  Created by Alexander Kunau on 30.10.24.
  6  //
  7  
  8  import SwiftUI
  9  
 10  struct ContentView: View {
 11      @ObservedObject var dataManager: FoodDataManager
 12      @ObservedObject var userProfile: UserProfile
 13      @ObservedObject var workoutManager: WorkoutManager
 14      @EnvironmentObject var appearanceSettings: AppearanceSettings
 15      @State private var showingAddFoodSheet = false
 16      
 17      var body: some View {
 18          TabView {
 19              NavigationStack {
 20                  DailyView(dataManager: dataManager, workoutManager: workoutManager, userProfile: userProfile)
 21                      .toolbar {
 22                          ToolbarItem(placement: .navigationBarTrailing) {
 23                              Button {
 24                                  showingAddFoodSheet = true
 25                              } label: {
 26                                  Label("Hinzufügen", systemImage: "plus")
 27                              }
 28                          }
 29                      }
 30              }
 31              .tabItem {
 32                  Label("Tagesübersicht", systemImage: "calendar")
 33              }
 34              
 35              NavigationStack {
 36                  MealPresetsListView(dataManager: dataManager)
 37              }
 38              .tabItem {
 39                  Label("Vorlagen", systemImage: "list.bullet")
 40              }
 41              
 42              NavigationStack {
 43                  ReportView(dataManager: dataManager)
 44              }
 45              .tabItem {
 46                  Label("Berichte", systemImage: "chart.bar.xaxis")
 47              }
 48              
 49              NavigationStack {
 50                  WorkoutView(workoutManager: workoutManager, userProfile: userProfile)
 51              }
 52              .tabItem {
 53                  Label("Workout", systemImage: "figure.run")
 54              }
 55              
 56              NavigationStack {
 57                  SettingsView(dataManager: dataManager, workoutManager: workoutManager, userProfile: userProfile)
 58              }
 59              .tabItem {
 60                  Label("Einstellungen", systemImage: "gear")
 61              }
 62          }
 63          .animation(.easeInOut(duration: 0.2), value: showingAddFoodSheet)
 64          .sheet(isPresented: $showingAddFoodSheet) {
 65              AddFoodView(dataManager: dataManager)
 66          }
 67          .preferredColorScheme(appearanceSettings.appearanceMode.colorScheme)
 68          .environmentObject(dataManager)
 69      }
 70  }
 71  
 72  struct MealPresetsListView: View {
 73      @ObservedObject var dataManager: FoodDataManager
 74      @State private var showingAddPreset = false
 75      @State private var selectedMealType: MealType? = nil
 76      @State private var addedPresetId: UUID? = nil
 77      @State private var showingAddedFeedback = false
 78      
 79      var body: some View {
 80          List {
 81              Section {
 82                  Picker("Mahlzeit filtern", selection: $selectedMealType) {
 83                      Text("Alle").tag(nil as MealType?)
 84                      ForEach(MealType.allCases, id: \.self) { type in
 85                          Label(type.rawValue, systemImage: type.icon).tag(type as MealType?)
 86                      }
 87                  }
 88                  .pickerStyle(.menu)
 89              }
 90              
 91              ForEach(dataManager.presets(for: selectedMealType)) { preset in
 92                  Section {
 93                      VStack(alignment: .leading, spacing: 12) {
 94                          // Header mit Mahlzeit-Typ und Name
 95                          VStack(alignment: .leading, spacing: 4) {
 96                              Label(preset.mealType.rawValue, systemImage: preset.mealType.icon)
 97                                  .font(.caption)
 98                                  .foregroundColor(.secondary)
 99                              
100                              Text(preset.name)
101                                  .font(.headline)
102                                  .fontWeight(.semibold)
103                          }
104                          
105                          Divider()
106                          
107                          // Gesamt-Nährwerte
108                          HStack(spacing: 16) {
109                              HStack(spacing: 4) {
110                                  Text("\(preset.totalCalories)")
111                                      .font(.title3)
112                                      .fontWeight(.bold)
113                                      .foregroundColor(.green)
114                                  Text("kcal")
115                                      .font(.caption)
116                                      .foregroundColor(.secondary)
117                              }
118                              
119                              Spacer()
120                              
121                              HStack(spacing: 12) {
122                                  Text("P: \(String(format: "%.0f", preset.totalProtein))g")
123                                      .font(.caption)
124                                      .foregroundColor(Color(red: 0xfe/255, green: 0x86/255, blue: 0x61/255))
125                                  
126                                  Text("K: \(String(format: "%.0f", preset.totalCarbs))g")
127                                      .font(.caption)
128                                      .foregroundColor(Color(red: 0x5D/255, green: 0xB9/255, blue: 0x85/255))
129                                  
130                                  Text("F: \(String(format: "%.0f", preset.totalFat))g")
131                                      .font(.caption)
132                                      .foregroundColor(Color(red: 0xFF/255, green: 0xD2/255, blue: 0x5F/255))
133                              }
134                          }
135                          .padding(.vertical, 4)
136                          
137                          Divider()
138                          
139                          // Einzelne Nahrungsmittel
140                          VStack(alignment: .leading, spacing: 8) {
141                              ForEach(preset.foods) { food in
142                                  HStack(alignment: .top) {
143                                      VStack(alignment: .leading, spacing: 2) {
144                                          Text(food.name)
145                                              .font(.subheadline)
146                                          
147                                          HStack(spacing: 8) {
148                                              Text("P: \(String(format: "%.0f", food.protein))g")
149                                                  .font(.caption2)
150                                                  .foregroundColor(Color(red: 0xfe/255, green: 0x86/255, blue: 0x61/255))
151                                              
152                                              Text("K: \(String(format: "%.0f", food.carbs))g")
153                                                  .font(.caption2)
154                                                  .foregroundColor(Color(red: 0x5D/255, green: 0xB9/255, blue: 0x85/255))
155                                              
156                                              Text("F: \(String(format: "%.0f", food.fat))g")
157                                                  .font(.caption2)
158                                                  .foregroundColor(Color(red: 0xFF/255, green: 0xD2/255, blue: 0x5F/255))
159                                          }
160                                      }
161                                      
162                                      Spacer()
163                                      
164                                      Text("\(food.calories)")
165                                          .font(.subheadline)
166                                          .fontWeight(.medium)
167                                          .foregroundColor(.secondary)
168                                  }
169                              }
170                          }
171                          
172                          Divider()
173                          
174                          // Button zum Hinzufügen
175                          Button {
176                              dataManager.addFoodItemsFromPreset(preset, date: Date())
177                              addedPresetId = preset.id
178                              showingAddedFeedback = true
179                              
180                              // Feedback nach 2 Sekunden ausblenden
181                              DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
182                                  if addedPresetId == preset.id {
183                                      showingAddedFeedback = false
184                                      addedPresetId = nil
185                                  }
186                              }
187                          } label: {
188                              HStack {
189                                  Spacer()
190                                  if addedPresetId == preset.id && showingAddedFeedback {
191                                      Image(systemName: "checkmark.circle.fill")
192                                          .foregroundColor(.green)
193                                      Text("Hinzugefügt!")
194                                          .font(.subheadline)
195                                          .fontWeight(.medium)
196                                          .foregroundColor(.green)
197                                  } else {
198                                      Image(systemName: "plus.circle.fill")
199                                          .foregroundColor(.blue)
200                                      Text("Heute hinzufügen")
201                                          .font(.subheadline)
202                                          .fontWeight(.medium)
203                                          .foregroundColor(.blue)
204                                  }
205                                  Spacer()
206                              }
207                              .padding(.vertical, 8)
208                              .background(
209                                  RoundedRectangle(cornerRadius: 8)
210                                      .fill(addedPresetId == preset.id && showingAddedFeedback ? Color.green.opacity(0.1) : Color.blue.opacity(0.1))
211                              )
212                              .animation(.easeInOut(duration: 0.3), value: showingAddedFeedback)
213                          }
214                          .disabled(addedPresetId == preset.id && showingAddedFeedback)
215                          .buttonStyle(.plain)
216                      }
217                      .padding(.vertical, 4)
218                  }
219              }
220              .onDelete { indexSet in
221                  let presetsToDelete = indexSet.map { dataManager.presets(for: selectedMealType)[$0] }
222                  for preset in presetsToDelete {
223                      dataManager.deletePreset(withId: preset.id)
224                  }
225              }
226              
227              if dataManager.mealPresets.isEmpty {
228                  Section {
229                      Text("Keine Vorlagen vorhanden")
230                          .foregroundColor(.secondary)
231                          .italic()
232                          .frame(maxWidth: .infinity, alignment: .center)
233                          .padding()
234                  }
235              }
236          }
237          .navigationTitle("Mahlzeiten-Vorlagen")
238          .toolbar {
239              ToolbarItem(placement: .navigationBarTrailing) {
240                  Button {
241                      showingAddPreset = true
242                  } label: {
243                      Label("Vorlage erstellen", systemImage: "plus")
244                  }
245              }
246          }
247          .sheet(isPresented: $showingAddPreset) {
248              MealPresetView(dataManager: dataManager)
249          }
250      }
251  }
252  
253  #Preview {
254      ContentView(
255          dataManager: FoodDataManager(),
256          userProfile: UserProfile(),
257          workoutManager: WorkoutManager()
258      )
259      .environmentObject(AppearanceSettings())
260  }