diff --git a/SleepFocus/Components/Settings/SettingsMainView.swift b/SleepFocus/Components/Settings/SettingsMainView.swift index 8bb2544..7201850 100644 --- a/SleepFocus/Components/Settings/SettingsMainView.swift +++ b/SleepFocus/Components/Settings/SettingsMainView.swift @@ -6,22 +6,24 @@ struct SettingsMainView: View { @EnvironmentObject var appearanceStore: AppearanceStore @StateObject private var healthAuth = HealthAuth() @AppStorage("settings.soundsEnabled") private var soundsEnabled = true - + var body: some View { ScrollView { VStack(spacing: 12) { SettingsHeaderView() - + ProfileSection(navigateTo: navigationManager.navigateTo) - + + BehaviorProfileSection() + NotificationsSection() - + HealthKitSection(healthAuth: healthAuth) - + SleepSettingsSection(navigateTo: navigationManager.navigateTo) - + FocusSettingsSection(navigateTo: navigationManager.navigateTo) - + AppSettingsSection( appearanceMode: Binding( get: { appearanceStore.mode }, @@ -29,16 +31,16 @@ struct SettingsMainView: View { ), soundsEnabled: $soundsEnabled ) - + GeneralSettingsSection(navigateTo: navigationManager.navigateTo) - + DeveloperSection( navigateTo: navigationManager.navigateTo, clearLocalScoreFetchStorage: clearLocalScoreFetchStorage ) - + SignOutSection() - + AppVersionFooter() } .padding(.horizontal, 16) @@ -54,6 +56,152 @@ struct SettingsMainView: View { DeveloperStorageTools.clearScoreFetchStorage() } } + +// MARK: - Behavior Profile Section + +struct BehaviorProfileSection: View { + @EnvironmentObject var behaviorStore: BehaviorProfileStore + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Text("Sleep Factors") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.appSecondaryText) + .padding(.horizontal, 4) + + VStack(spacing: 0) { + ForEach(allQuestions) { question in + BehaviorFactorRow( + question: question, + answer: behaviorStore.profile[keyPath: question.keyPath], + onSelect: { newAnswer in + behaviorStore.profile[keyPath: question.keyPath] = newAnswer + behaviorStore.save() + } + ) + + if question.id != allQuestions.last?.id { + Divider().padding(.leading, 56) + } + } + } + .background(Color.appCardBackground) + .cornerRadius(12) + .shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1) + } + } +} + +// MARK: - Behavior Factor Row + +private struct BehaviorFactorRow: View { + let question: BehaviorQuestion + let answer: BehaviorAnswer + let onSelect: (BehaviorAnswer) -> Void + + @State private var isExpanded = false + + var body: some View { + VStack(spacing: 0) { + Button(action: { + withAnimation(.easeInOut(duration: 0.2)) { isExpanded.toggle() } + }) { + HStack(spacing: 12) { + Image(systemName: question.categoryIcon) + .font(.system(size: 16)) + .foregroundColor(.systemIndigo) + .frame(width: 28) + + VStack(alignment: .leading, spacing: 2) { + Text(question.question) + .font(.system(size: 15)) + .foregroundColor(.appPrimaryText) + .multilineTextAlignment(.leading) + + Text(answer.rawValue) + .font(.system(size: 13)) + .foregroundColor(.appSecondaryText) + } + + Spacer() + + Image(systemName: isExpanded ? "chevron.up" : "chevron.down") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.appSecondaryText) + } + .padding(.horizontal, 16) + .padding(.vertical, 12) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + + if isExpanded { + VStack(spacing: 8) { + ForEach(BehaviorAnswer.allCases, id: \.self) { option in + Button(action: { + onSelect(option) + withAnimation { isExpanded = false } + }) { + HStack { + Text(labelText(for: option)) + .font(.system(size: 14, weight: .medium)) + .foregroundColor(answer == option ? .white : .appPrimaryText) + Spacer() + if answer == option { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.white) + } + } + .padding(.horizontal, 16) + .padding(.vertical, 10) + .background(answer == option ? Color.systemIndigo : Color.appCardSecondaryBackground) + .cornerRadius(10) + } + .buttonStyle(.plain) + } + } + .padding(.horizontal, 12) + .padding(.bottom, 12) + .transition(.opacity.combined(with: .move(edge: .top))) + } + } + } + + private func labelText(for option: BehaviorAnswer) -> String { + switch option { + case .yes: return question.yesLabel + case .sometimes: return "Sometimes" + case .no: return question.noLabel + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + +