fix: dashboard calorie widget horizontal layout + bold meal section headers
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Security Checks / dockerfile-lint (push) Successful in 3s

Dashboard:
- Removed LazyVGrid, calorie widget is full-width HStack
- Ring (90px) + text info side by side
- No more vertical text issue

Meal sections:
- Title3 bold font for meal name
- Meal icon in tinted circle background
- Item count subtitle
- Calorie total in meal color (bold)
- Subtle tinted background on header
- Clear visual separation from food entries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-03 08:44:53 -05:00
parent db6324f161
commit 3040e55475
2 changed files with 61 additions and 34 deletions

View File

@@ -19,41 +19,51 @@ struct MealSectionView: View {
isExpanded.toggle() isExpanded.toggle()
} }
} label: { } label: {
HStack(spacing: 12) { HStack(spacing: 0) {
// Accent bar // Colored accent bar
RoundedRectangle(cornerRadius: 2) RoundedRectangle(cornerRadius: 2)
.fill(Color.mealColor(for: mealType.rawValue)) .fill(Color.mealColor(for: mealType.rawValue))
.frame(width: 4, height: 32) .frame(width: 4, height: 40)
.padding(.trailing, 12)
// Meal icon in tinted circle
Image(systemName: mealType.icon) Image(systemName: mealType.icon)
.font(.body.weight(.medium)) .font(.title3.weight(.semibold))
.foregroundStyle(Color.mealColor(for: mealType.rawValue)) .foregroundStyle(Color.mealColor(for: mealType.rawValue))
.frame(width: 32, height: 32)
.background(Color.mealColor(for: mealType.rawValue).opacity(0.12))
.clipShape(Circle())
.padding(.trailing, 10)
VStack(alignment: .leading, spacing: 2) {
Text(mealType.displayName) Text(mealType.displayName)
.font(.headline) .font(.title3.weight(.bold))
.foregroundStyle(Color.textPrimary) .foregroundStyle(Color.textPrimary)
Text("\(entries.count)") Text("\(entries.count) item\(entries.count == 1 ? "" : "s")")
.font(.caption.weight(.medium)) .font(.caption)
.foregroundStyle(Color.textSecondary) .foregroundStyle(Color.textTertiary)
.padding(.horizontal, 6) }
.padding(.vertical, 2)
.background(Color.textSecondary.opacity(0.1))
.clipShape(Capsule())
Spacer() Spacer()
Text("\(Int(totalCalories)) kcal") Text("\(Int(totalCalories))")
.font(.subheadline.weight(.semibold)) .font(.title3.weight(.bold))
.foregroundStyle(Color.mealColor(for: mealType.rawValue))
+ Text(" kcal")
.font(.caption.weight(.medium))
.foregroundStyle(Color.textSecondary) .foregroundStyle(Color.textSecondary)
Image(systemName: "chevron.right") Image(systemName: "chevron.right")
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundStyle(Color.textTertiary) .foregroundStyle(Color.textTertiary)
.rotationEffect(.degrees(isExpanded ? 90 : 0)) .rotationEffect(.degrees(isExpanded ? 90 : 0))
.padding(.leading, 8)
} }
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.vertical, 12) .padding(.vertical, 14)
.background(Color.mealColor(for: mealType.rawValue).opacity(0.04))
.clipShape(RoundedRectangle(cornerRadius: 14))
} }
if isExpanded { if isExpanded {

View File

@@ -4,10 +4,10 @@ import PhotosUI
struct HomeView: View { struct HomeView: View {
@Environment(AuthManager.self) private var auth @Environment(AuthManager.self) private var auth
@State private var vm = HomeViewModel() @State private var vm = HomeViewModel()
@State private var showProfileMenu = false @State private var showAssistant = false
var body: some View { var body: some View {
ZStack { ZStack(alignment: .bottomTrailing) {
// Background // Background
if let bg = vm.backgroundImage { if let bg = vm.backgroundImage {
Image(uiImage: bg) Image(uiImage: bg)
@@ -23,7 +23,7 @@ struct HomeView: View {
// Top bar // Top bar
HStack { HStack {
Text("Home") Text("Home")
.font(.largeTitle.weight(.bold)) .font(.title.weight(.bold))
.foregroundStyle(vm.hasBackground ? .white : Color.textPrimary) .foregroundStyle(vm.hasBackground ? .white : Color.textPrimary)
Spacer() Spacer()
Menu { Menu {
@@ -55,22 +55,36 @@ struct HomeView: View {
.padding(.horizontal) .padding(.horizontal)
.padding(.top, 60) .padding(.top, 60)
// Widget grid // Calorie widget full width card
LazyVGrid(columns: [
GridItem(.flexible(), spacing: 12),
GridItem(.flexible(), spacing: 12),
], spacing: 12) {
// Calorie widget
calorieWidget calorieWidget
.gridCellColumns(2)
}
.padding(.horizontal) .padding(.horizontal)
Spacer(minLength: 100) Spacer(minLength: 100)
} }
} }
// Floating + button
Button {
showAssistant = true
} label: {
Image(systemName: "plus")
.font(.title2.weight(.semibold))
.foregroundStyle(.white)
.frame(width: 56, height: 56)
.background(Color.accentWarm)
.clipShape(Circle())
.shadow(color: Color.accentWarm.opacity(0.3), radius: 8, y: 4)
}
.padding(.trailing, 20)
.padding(.bottom, 100)
} }
.navigationBarHidden(true) .navigationBarHidden(true)
.sheet(isPresented: $showAssistant) {
FoodSearchView(
date: Date().apiDateString,
onFoodAdded: { Task { await vm.loadTodayData() } }
)
}
.task { .task {
await vm.loadTodayData() await vm.loadTodayData()
} }
@@ -81,22 +95,25 @@ struct HomeView: View {
private var calorieWidget: some View { private var calorieWidget: some View {
HStack(spacing: 20) { HStack(spacing: 20) {
// Ring
MacroRingWithLabel( MacroRingWithLabel(
consumed: vm.totalCalories, consumed: vm.totalCalories,
goal: vm.calorieGoal, goal: vm.calorieGoal,
label: "kcal", label: "kcal",
color: .emerald, color: .emerald,
size: 100, size: 90,
lineWidth: 10 lineWidth: 9
) )
.frame(width: 90, height: 90)
VStack(alignment: .leading, spacing: 8) { // Text info
VStack(alignment: .leading, spacing: 6) {
Text("Calories") Text("Calories")
.font(.headline) .font(.headline)
.foregroundStyle(vm.hasBackground ? .white : Color.textPrimary) .foregroundStyle(vm.hasBackground ? .white : Color.textPrimary)
Text("\(Int(vm.totalCalories)) / \(Int(vm.calorieGoal))") Text("\(Int(vm.totalCalories)) / \(Int(vm.calorieGoal))")
.font(.subheadline) .font(.subheadline.weight(.medium))
.foregroundStyle(vm.hasBackground ? .white.opacity(0.8) : Color.textSecondary) .foregroundStyle(vm.hasBackground ? .white.opacity(0.8) : Color.textSecondary)
let remaining = max(vm.calorieGoal - vm.totalCalories, 0) let remaining = max(vm.calorieGoal - vm.totalCalories, 0)