/ 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 }