import SwiftUI import SwiftData struct OnboardingView: View { @Environment(\.modelContext) private var context @StateObject private var viewModel = OnboardingViewModel() @Query(sort: \Tag.sortOrder) private var tags: [Tag] var onComplete: () -> Void var body: some View { ZStack { Color.mealMoodBackground.ignoresSafeArea() VStack(spacing: 0) { onboardingHeader HStack(spacing: 8) { ForEach(1..() if (try? context.fetch(descriptor))?.isEmpty ?? true { DefaultDataService.createDefaultTags(context: context) } } private func restoreFromICloudAndFinishIfNeeded() async { let settingsDescriptor = FetchDescriptor() let currentSettings = (try? context.fetch(settingsDescriptor))?.first let iCloudEnabled = currentSettings?.iCloudSyncEnabledResolved ?? viewModel.syncICloud guard iCloudEnabled else { return } await ICloudSyncService.shared.pullRemoteIfNeeded(context: context) guard shouldAutoCompleteOnboarding() else { return } if let settings = (try? context.fetch(settingsDescriptor))?.first, !settings.onboardingCompleted { settings.onboardingCompleted = true try? context.save() } onComplete() } private func shouldAutoCompleteOnboarding() -> Bool { let dishes = (try? context.fetch(FetchDescriptor())) ?? [] if !dishes.isEmpty { return true } let plans = (try? context.fetch(FetchDescriptor())) ?? [] return !plans.isEmpty } private var onboardingHeader: some View { VStack(spacing: 6) { HStack { if viewModel.currentStep > 0 { Button { viewModel.previousStep() } label: { Image(systemName: "chevron.left") .font(.system(size: 16, weight: .semibold)) .foregroundColor(.mealMoodTextPrimary) .frame(width: 32, height: 32) .background(Color.mealMoodSurface) .clipShape(Circle()) } .buttonStyle(.plain) } else { Color.clear.frame(width: 32, height: 32) } Spacer() Text(headerTitle) .font(.mealMoodBodyBold) .foregroundColor(.mealMoodTextPrimary) .lineLimit(1) Spacer() Text(stepCounterText) .font(.mealMoodCaption) .foregroundColor(.mealMoodTextSecondary) .frame(width: 70, alignment: .trailing) } } .padding(.horizontal, 16) .padding(.top, 8) } private var headerTitle: String { guard viewModel.currentStep > 0 else { return String(localized: "onboarding_nav_intro") } return String(format: String(localized: "onboarding_nav_step_short"), viewModel.currentStep) } private var stepCounterText: String { guard viewModel.currentStep > 0 else { return String(localized: "onboarding_nav_setup") } return String(format: String(localized: "onboarding_nav_step_counter"), viewModel.currentStep, viewModel.totalSteps - 1) } }