import SwiftUI struct ProfileView: View { @EnvironmentObject var authManager: AuthManager @State private var showChangePassword = false var body: some View { ZStack { Color(hex: "0a0a1a").ignoresSafeArea() VStack(spacing: 0) { // Header VStack(spacing: 12) { ZStack { Circle().fill(Color(hex: "00d4aa").opacity(0.2)).frame(width: 80, height: 80) Text(String(authManager.userName.prefix(1)).uppercased()) .font(.largeTitle.bold()).foregroundColor(Color(hex: "00d4aa")) } Text(authManager.userName).font(.title2.bold()).foregroundColor(.white) } .padding(.top, 40).padding(.bottom, 32) // Settings list VStack(spacing: 2) { ProfileRow(icon: "lock.fill", title: "Сменить пароль", color: "7c3aed") { showChangePassword = true } ProfileRow(icon: "heart.fill", title: "Health API ключ", subtitle: authManager.healthApiKey, color: "ff4757") {} Divider().background(Color.white.opacity(0.1)).padding(.vertical, 8) ProfileRow(icon: "rectangle.portrait.and.arrow.right", title: "Выйти", color: "ff4757", isDestructive: true) { authManager.logout() } } .padding(.horizontal) Spacer() Text("Pulse v1.0").font(.caption).foregroundColor(Color(hex: "8888aa")).padding(.bottom, 20) } } .sheet(isPresented: $showChangePassword) { ChangePasswordView(isPresented: $showChangePassword) } } } struct ProfileRow: View { let icon: String let title: String var subtitle: String? = nil let color: String var isDestructive: Bool = false 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) } VStack(alignment: .leading, spacing: 2) { Text(title).foregroundColor(isDestructive ? Color(hex: "ff4757") : .white).font(.callout) if let sub = subtitle { Text(sub).font(.caption).foregroundColor(Color(hex: "8888aa")) } } Spacer() if !isDestructive { Image(systemName: "chevron.right").foregroundColor(Color(hex: "8888aa")).font(.caption) } } .padding(14) .background(RoundedRectangle(cornerRadius: 12).fill(Color.white.opacity(0.05))) } } } struct ChangePasswordView: View { @Binding var isPresented: Bool @EnvironmentObject var authManager: AuthManager @State private var oldPassword = "" @State private var newPassword = "" @State private var confirm = "" @State private var isLoading = false @State private var errorMessage = "" @State private var success = false var body: some View { NavigationView { ZStack { Color(hex: "0a0a1a").ignoresSafeArea() VStack(spacing: 16) { if success { VStack(spacing: 12) { Text("✅").font(.system(size: 50)) Text("Пароль изменён!").font(.title2.bold()).foregroundColor(.white) }.padding(.top, 40) Button("Закрыть") { isPresented = false }.foregroundColor(Color(hex: "00d4aa")) } else { SecureField("Текущий пароль", text: $oldPassword) .padding().background(Color.white.opacity(0.08)).cornerRadius(12).foregroundColor(.white) SecureField("Новый пароль", text: $newPassword) .padding().background(Color.white.opacity(0.08)).cornerRadius(12).foregroundColor(.white) SecureField("Подтвердите пароль", text: $confirm) .padding().background(Color.white.opacity(0.08)).cornerRadius(12).foregroundColor(.white) if !errorMessage.isEmpty { Text(errorMessage).foregroundColor(.red).font(.caption) } Button(action: change) { if isLoading { ProgressView().tint(.black) } else { Text("Сменить").font(.headline).foregroundColor(.black) } } .frame(maxWidth: .infinity).padding() .background(Color(hex: "00d4aa")).cornerRadius(12) .disabled(oldPassword.isEmpty || newPassword.isEmpty || isLoading) } Spacer() }.padding() } .navigationTitle("Смена пароля") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Отмена") { isPresented = false } } } .preferredColorScheme(.dark) } } func change() { guard newPassword == confirm else { errorMessage = "Пароли не совпадают"; return } guard newPassword.count >= 8 else { errorMessage = "Минимум 8 символов"; return } isLoading = true Task { var req = URLRequest(url: URL(string: "https://api.digital-home.site/auth/password")!) req.httpMethod = "PUT" req.setValue("application/json", forHTTPHeaderField: "Content-Type") req.setValue("Bearer \(authManager.token)", forHTTPHeaderField: "Authorization") req.httpBody = try? JSONEncoder().encode(["old_password": oldPassword, "new_password": newPassword]) let (_, response) = (try? await URLSession.shared.data(for: req)) ?? (Data(), nil) await MainActor.run { if (response as? HTTPURLResponse)?.statusCode == 200 { success = true } else { errorMessage = "Неверный текущий пароль" } isLoading = false } } } }