restore: original UI views from first build, keep fixed models/API
All checks were successful
Security Checks / dependency-audit (push) Successful in 12s
Security Checks / secret-scanning (push) Successful in 3s
Security Checks / dockerfile-lint (push) Successful in 3s

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-03 01:54:46 -05:00
parent e852e98812
commit fdb8aeba8a
61 changed files with 6652 additions and 1178 deletions

View File

@@ -0,0 +1,188 @@
import SwiftUI
struct HomeView: View {
@Environment(AuthManager.self) private var authManager
@State private var viewModel = HomeViewModel()
var body: some View {
NavigationStack {
ScrollView {
VStack(spacing: 20) {
if viewModel.isLoading {
LoadingView(message: "Loading dashboard...")
.frame(height: 300)
} else {
// Quick Stats Card
caloriesSummaryCard
// Macros Card
macrosCard
// Quick Actions
quickActionsCard
}
if let error = viewModel.errorMessage {
ErrorBanner(message: error) {
Task { await viewModel.load() }
}
}
}
.padding(16)
}
.background(Color.canvas)
.navigationTitle("Dashboard")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Menu {
Button(role: .destructive) {
authManager.logout()
} label: {
Label("Sign Out", systemImage: "rectangle.portrait.and.arrow.right")
}
} label: {
Image(systemName: "person.circle.fill")
.font(.title3)
.foregroundStyle(Color.accentWarm)
}
}
}
.refreshable {
await viewModel.load()
}
.task {
await viewModel.load()
}
}
}
private var caloriesSummaryCard: some View {
VStack(spacing: 16) {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("Today")
.font(.headline)
.foregroundStyle(Color.text1)
Text(Date().displayString)
.font(.subheadline)
.foregroundStyle(Color.text3)
}
Spacer()
Text("\(viewModel.entryCount) entries")
.font(.caption)
.foregroundStyle(Color.text4)
.padding(.horizontal, 10)
.padding(.vertical, 4)
.background(Color.surfaceSecondary)
.clipShape(Capsule())
}
MacroRingLarge(
current: viewModel.totalCalories,
goal: viewModel.goal.calories,
color: .caloriesColor,
size: 140,
lineWidth: 12
)
HStack(spacing: 0) {
macroStat("Eaten", value: Int(viewModel.totalCalories), unit: "kcal")
Spacer()
macroStat("Remaining", value: Int(max(viewModel.goal.calories - viewModel.totalCalories, 0)), unit: "kcal")
Spacer()
macroStat("Goal", value: Int(viewModel.goal.calories), unit: "kcal")
}
}
.padding(20)
.background(Color.surface)
.clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(color: .black.opacity(0.04), radius: 8, y: 4)
}
private var macrosCard: some View {
VStack(spacing: 14) {
Text("Macros")
.font(.headline)
.foregroundStyle(Color.text1)
.frame(maxWidth: .infinity, alignment: .leading)
HStack(spacing: 20) {
MacroRing(
current: viewModel.totalProtein,
goal: viewModel.goal.protein,
color: .proteinColor,
label: "Protein",
unit: "g",
size: 68
)
MacroRing(
current: viewModel.totalCarbs,
goal: viewModel.goal.carbs,
color: .carbsColor,
label: "Carbs",
unit: "g",
size: 68
)
MacroRing(
current: viewModel.totalFat,
goal: viewModel.goal.fat,
color: .fatColor,
label: "Fat",
unit: "g",
size: 68
)
}
.frame(maxWidth: .infinity)
}
.padding(20)
.background(Color.surface)
.clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(color: .black.opacity(0.04), radius: 8, y: 4)
}
private var quickActionsCard: some View {
VStack(spacing: 12) {
Text("Quick Actions")
.font(.headline)
.foregroundStyle(Color.text1)
.frame(maxWidth: .infinity, alignment: .leading)
HStack(spacing: 12) {
quickActionButton(icon: "plus.circle.fill", label: "Log Food", color: .accentEmerald)
quickActionButton(icon: "doc.text.fill", label: "Templates", color: .carbsColor)
quickActionButton(icon: "clock.fill", label: "History", color: .accentWarm)
}
}
.padding(20)
.background(Color.surface)
.clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(color: .black.opacity(0.04), radius: 8, y: 4)
}
private func macroStat(_ label: String, value: Int, unit: String) -> some View {
VStack(spacing: 2) {
Text("\(value)")
.font(.system(.title3, design: .rounded, weight: .bold))
.foregroundStyle(Color.text1)
Text("\(label)")
.font(.caption2)
.foregroundStyle(Color.text4)
}
}
private func quickActionButton(icon: String, label: String, color: Color) -> some View {
VStack(spacing: 8) {
Image(systemName: icon)
.font(.title2)
.foregroundStyle(color)
Text(label)
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(Color.text2)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 16)
.background(color.opacity(0.06))
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}

View File

@@ -0,0 +1,49 @@
import Foundation
@MainActor @Observable
final class HomeViewModel {
var todayEntries: [FoodEntry] = []
var goal: DailyGoal = .defaultGoal
var isLoading = true
var errorMessage: String?
private let repo = FitnessRepository.shared
var totalCalories: Double {
todayEntries.reduce(0) { $0 + $1.calories }
}
var totalProtein: Double {
todayEntries.reduce(0) { $0 + $1.protein }
}
var totalCarbs: Double {
todayEntries.reduce(0) { $0 + $1.carbs }
}
var totalFat: Double {
todayEntries.reduce(0) { $0 + $1.fat }
}
var entryCount: Int {
todayEntries.count
}
func load() async {
isLoading = true
errorMessage = nil
let today = Date().apiDateString
do {
async let entriesTask = repo.entries(for: today, forceRefresh: true)
async let goalsTask = repo.goals(for: today)
todayEntries = try await entriesTask
goal = try await goalsTask
} catch {
errorMessage = error.localizedDescription
}
isLoading = false
}
}