import SwiftUI // MARK: - SettingsView struct SettingsView: View { @EnvironmentObject var authManager: AuthManager @AppStorage("colorScheme") private var colorSchemeRaw: String = "dark" @State private var profile: UserProfile? @State private var isLoading = true @State private var isSaving = false @State private var showPasswordChange = false // Profile fields @State private var telegramChatId = "" @State private var morningNotification = true @State private var eveningNotification = true @State private var morningTime = "09:00" @State private var eveningTime = "21:00" @State private var timezone = "Europe/Moscow" @State private var username = "" var isDark: Bool { colorSchemeRaw != "light" } let timezones = [ "Europe/Moscow", "Europe/Kaliningrad", "Europe/Samara", "Asia/Yekaterinburg", "Asia/Omsk", "Asia/Krasnoyarsk", "Asia/Irkutsk", "Asia/Yakutsk", "Asia/Vladivostok", "Asia/Magadan", "Asia/Kamchatka", "UTC", "Europe/London", "Europe/Berlin", "Europe/Paris", "America/New_York" ] var body: some View { ZStack { Color(hex: "0a0a1a").ignoresSafeArea() ScrollView { VStack(spacing: 0) { // Header / Avatar VStack(spacing: 12) { ZStack { Circle().fill(Color(hex: "0D9488").opacity(0.2)).frame(width: 80, height: 80) Text(String(authManager.userName.prefix(1)).uppercased()) .font(.largeTitle.bold()).foregroundColor(Color(hex: "0D9488")) } Text(authManager.userName).font(.title2.bold()).foregroundColor(.white) Text("ID: \(authManager.userId)").font(.caption).foregroundColor(Color(hex: "8888aa")) } .padding(.top, 24) .padding(.bottom, 28) if isLoading { ProgressView().tint(Color(hex: "0D9488")).padding(.top, 20) } else { VStack(spacing: 20) { // MARK: Appearance SettingsSection(title: "Внешний вид") { SettingsToggle(icon: "moon.fill", title: isDark ? "Тёмная тема" : "Светлая тема", color: "6366f1", isOn: isDark) { colorSchemeRaw = isDark ? "light" : "dark" } } // MARK: Profile SettingsSection(title: "Профиль") { VStack(spacing: 8) { Label("Имя пользователя", systemImage: "person.fill") .font(.caption).foregroundColor(Color(hex: "8888aa")) .frame(maxWidth: .infinity, alignment: .leading) TextField("Username", text: $username) .foregroundColor(.white).padding(14) .background(RoundedRectangle(cornerRadius: 12).fill(Color.white.opacity(0.07))) } .padding(.horizontal, 4) SettingsButton(icon: "lock.fill", title: "Сменить пароль", color: "7c3aed") { showPasswordChange = true } } // MARK: Telegram SettingsSection(title: "Telegram Бот") { VStack(alignment: .leading, spacing: 8) { Label("Chat ID", systemImage: "paperplane.fill") .font(.caption).foregroundColor(Color(hex: "8888aa")) TextField("Например: 123456789", text: $telegramChatId) .keyboardType(.numbersAndPunctuation) .foregroundColor(.white).padding(14) .background(RoundedRectangle(cornerRadius: 12).fill(Color.white.opacity(0.07))) HStack(spacing: 4) { Image(systemName: "info.circle").font(.caption2).foregroundColor(Color(hex: "0D9488")) Text("Напишите /start боту @pulse_tracking_bot, чтобы получить Chat ID") .font(.caption2).foregroundColor(Color(hex: "8888aa")) } } .padding(.horizontal, 4) } // MARK: Notifications SettingsSection(title: "Уведомления") { VStack(spacing: 12) { SettingsToggle(icon: "sunrise.fill", title: "Утренние уведомления", color: "ffa502", isOn: morningNotification) { morningNotification.toggle() } if morningNotification { HStack { Text("Время").font(.callout).foregroundColor(Color(hex: "8888aa")) Spacer() TextField("09:00", text: $morningTime) .keyboardType(.numbersAndPunctuation) .foregroundColor(.white) .multilineTextAlignment(.trailing) .frame(width: 60) } .padding(.horizontal, 4) } Divider().background(Color.white.opacity(0.08)) SettingsToggle(icon: "moon.stars.fill", title: "Вечерние уведомления", color: "6366f1", isOn: eveningNotification) { eveningNotification.toggle() } if eveningNotification { HStack { Text("Время").font(.callout).foregroundColor(Color(hex: "8888aa")) Spacer() TextField("21:00", text: $eveningTime) .keyboardType(.numbersAndPunctuation) .foregroundColor(.white) .multilineTextAlignment(.trailing) .frame(width: 60) } .padding(.horizontal, 4) } } } // MARK: Timezone SettingsSection(title: "Часовой пояс") { VStack(alignment: .leading, spacing: 8) { Label("Выберите часовой пояс", systemImage: "clock.fill") .font(.caption).foregroundColor(Color(hex: "8888aa")) Picker("Часовой пояс", selection: $timezone) { ForEach(timezones, id: \.self) { tz in Text(tz).tag(tz) } } .pickerStyle(.wheel) .frame(height: 120) .clipped() .background(RoundedRectangle(cornerRadius: 12).fill(Color.white.opacity(0.04))) } .padding(.horizontal, 4) } // MARK: Save Button Button(action: { Task { await saveProfile() } }) { HStack { if isSaving { ProgressView().tint(.black).scaleEffect(0.8) } else { Text("Сохранить изменения").font(.headline).foregroundColor(.black) } } .frame(maxWidth: .infinity).padding() .background(LinearGradient(colors: [Color(hex: "0D9488"), Color(hex: "14b8a6")], startPoint: .leading, endPoint: .trailing)) .cornerRadius(14) } .disabled(isSaving) .padding(.horizontal) // MARK: Logout Button(action: { authManager.logout() }) { HStack { Image(systemName: "rectangle.portrait.and.arrow.right") Text("Выйти из аккаунта") } .font(.callout.bold()) .foregroundColor(Color(hex: "ff4757")) .frame(maxWidth: .infinity).padding() .background(RoundedRectangle(cornerRadius: 14).fill(Color(hex: "ff4757").opacity(0.1))) .overlay(RoundedRectangle(cornerRadius: 14).stroke(Color(hex: "ff4757").opacity(0.3), lineWidth: 1)) } .padding(.horizontal) Text("Pulse v1.1 • Made with ❤️").font(.caption).foregroundColor(Color(hex: "8888aa")) .padding(.bottom, 20) } } } } } .task { await loadProfile() } .sheet(isPresented: $showPasswordChange) { ChangePasswordView(isPresented: $showPasswordChange) .presentationDetents([.medium]) .presentationDragIndicator(.visible) .presentationBackground(Color(hex: "0a0a1a")) } } func loadProfile() async { isLoading = true username = authManager.userName if let p = try? await APIService.shared.getProfile(token: authManager.token) { profile = p telegramChatId = p.telegramChatId ?? "" morningNotification = p.morningNotification ?? true eveningNotification = p.eveningNotification ?? true morningTime = p.morningTime ?? "09:00" eveningTime = p.eveningTime ?? "21:00" timezone = p.timezone ?? "Europe/Moscow" } isLoading = false } func saveProfile() async { isSaving = true let req = UpdateProfileRequest( telegramChatId: telegramChatId.isEmpty ? nil : telegramChatId, morningNotification: morningNotification, eveningNotification: eveningNotification, morningTime: morningTime, eveningTime: eveningTime, timezone: timezone ) _ = try? await APIService.shared.updateProfile(token: authManager.token, request: req) // Update username if changed if username != authManager.userName { var req2 = URLRequest(url: URL(string: "https://api.digital-home.site/auth/me")!) req2.httpMethod = "PUT" req2.setValue("application/json", forHTTPHeaderField: "Content-Type") req2.setValue("Bearer \(authManager.token)", forHTTPHeaderField: "Authorization") req2.httpBody = try? JSONEncoder().encode(["username": username]) _ = try? await URLSession.shared.data(for: req2) await MainActor.run { authManager.userName = username; UserDefaults.standard.set(username, forKey: "userName") } } isSaving = false } } // MARK: - SettingsSection struct SettingsSection: View { let title: String @ViewBuilder let content: () -> Content var body: some View { VStack(alignment: .leading, spacing: 12) { Text(title).font(.subheadline.bold()).foregroundColor(Color(hex: "8888aa")).padding(.horizontal) VStack(spacing: 8) { content() } .padding(16) .background(RoundedRectangle(cornerRadius: 16).fill(Color.white.opacity(0.04))) .padding(.horizontal) } } } // MARK: - SettingsToggle struct SettingsToggle: View { let icon: String let title: String let color: String let isOn: Bool let onToggle: () -> Void var body: some View { HStack(spacing: 14) { ZStack { RoundedRectangle(cornerRadius: 8).fill(Color(hex: color).opacity(0.2)).frame(width: 36, height: 36) Image(systemName: icon).foregroundColor(Color(hex: color)).font(.subheadline) } Text(title).font(.callout).foregroundColor(.white) Spacer() Toggle("", isOn: Binding(get: { isOn }, set: { _ in onToggle() })) .tint(Color(hex: "0D9488")) } } } // MARK: - SettingsButton struct SettingsButton: View { let icon: String let title: String let color: String let action: () -> Void var body: some View { Button(action: action) { HStack(spacing: 14) { ZStack { RoundedRectangle(cornerRadius: 8).fill(Color(hex: color).opacity(0.2)).frame(width: 36, height: 36) Image(systemName: icon).foregroundColor(Color(hex: color)).font(.subheadline) } Text(title).font(.callout).foregroundColor(.white) Spacer() Image(systemName: "chevron.right").foregroundColor(Color(hex: "8888aa")).font(.caption) } } } }