#if os(Android) import SwiftUI struct UserProfileView: View { @Binding var appState: AppState @State var name: String = "" @State var email: String = "" @State var phone: String = "" @State var profilePicture: String = "" @State var isSaving = false var body: some View { ScrollView(showsIndicators: false) { VStack(spacing: 22) { avatarSection formSection saveButton } .padding(.horizontal, 20) .padding(.top, 18) .padding(.bottom, UIDevice.bottomNotch + 24) } .background(AppColors.backgroundLight) .navigationTitle("Meu Perfil") .appInlineNavigationTitle() .onAppear { hydrateFromAppState() } } private var avatarSection: some View { VStack(spacing: 12) { Circle() .fill(AppColors.brandSoft) .frame(width: 110, height: 110) .overlay { Text(initials) .font(.system(size: 32, weight: .bold)) .foregroundStyle(AppColors.primary) } Button("Remover") { profilePicture = "" } .font(AppTypography.caption) .foregroundStyle(AppColors.textMuted) .buttonStyle(.plain) .disabled(profilePicture.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) } .frame(maxWidth: .infinity) } private var formSection: some View { VStack(alignment: .leading, spacing: 14) { textFieldSection(title: "Nome", placeholder: "Seu nome completo", text: $name) textFieldSection(title: "E-mail", placeholder: "seu@email.com", text: $email) .appNoAutoCap() textFieldSection(title: "Telefone", placeholder: "(00) 00000-0000", text: $phone) .onChange(of: phone) { _, newValue in let masked = formatPhoneBR(displayPhoneDigits(newValue)) if masked != newValue { phone = masked } } .appNoAutoCap() } .padding(16) .background(AppColors.surface) .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)) } private var saveButton: some View { Button(isSaving ? "Salvando..." : "Salvar Alterações") { Task { await saveProfile() } } .font(AppTypography.button) .foregroundStyle(AppColors.textInverse) .frame(maxWidth: .infinity, minHeight: 56) .background(AppColors.primary) .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)) .buttonStyle(.plain) .disabled(isSaving || canSave == false) .opacity((isSaving || canSave == false) ? 0.6 : 1.0) } private var initials: String { let parts = name .trimmingCharacters(in: .whitespacesAndNewlines) .split(separator: " ") .prefix(2) let letters = parts.compactMap { $0.first }.map(String.init).joined() return letters.isEmpty ? "PF" : letters.uppercased() } private var canSave: Bool { let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines) let cleanEmail = email.trimmingCharacters(in: .whitespacesAndNewlines) let normalizedPhone = normalizePhoneNumberForAPI(phone) return cleanName.isEmpty == false && cleanEmail.isEmpty == false && cleanEmail.contains("@") && normalizedPhone.isEmpty == false } private func hydrateFromAppState() { name = appState.profile.name email = appState.profile.email phone = formatPhoneForDisplay(appState.profile.phone) profilePicture = appState.profile.profilePicture } @MainActor private func saveProfile() async { guard canSave else { return } let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines) let cleanEmail = email.trimmingCharacters(in: .whitespacesAndNewlines) let normalizedPhone = normalizePhoneNumberForAPI(phone) let cleanPhoto = profilePicture.trimmingCharacters(in: .whitespacesAndNewlines) isSaving = true defer { isSaving = false } do { let response = try await ApiService().updateCustomerProfile( name: cleanName, email: cleanEmail, phoneNumber: normalizedPhone, profilePicture: cleanPhoto.isEmpty ? nil : cleanPhoto ) if response.error { SnackbarCenter.shared.show( title: response.message ?? "Não foi possível atualizar seu perfil.", style: .error, icon: "xmark.octagon.fill", duration: 3.0 ) return } let customer = response.result appState.profile.id = customer?.id ?? appState.profile.id appState.profile.name = customer?.name ?? cleanName appState.profile.email = customer?.email ?? cleanEmail appState.profile.phone = customer?.phoneNumber ?? normalizedPhone appState.profile.profilePicture = customer?.profilePicture ?? cleanPhoto SessionStateStore.setActiveUserKey( SessionStateStore.makeUserKey(profileId: appState.profile.id, email: appState.profile.email) ) SnackbarCenter.shared.show( title: "Perfil atualizado com sucesso.", style: .success, icon: "checkmark.circle.fill", duration: 2.0 ) } catch { SnackbarCenter.shared.show( title: "Não foi possível atualizar seu perfil.", style: .error, icon: "xmark.octagon.fill", duration: 3.0 ) } } private func formatPhoneForDisplay(_ raw: String) -> String { let digits = displayPhoneDigits(raw) if digits.isEmpty { return "" } return formatPhoneBR(digits) } private func displayPhoneDigits(_ raw: String) -> String { var digits = raw.filter(\.isNumber) if digits.hasPrefix("55"), digits.count > 11 { digits = String(digits.dropFirst(2)) } return String(digits.prefix(11)) } private func textFieldSection(title: String, placeholder: String, text: Binding) -> some View { VStack(alignment: .leading, spacing: 8) { Text(title) .font(AppTypography.caption) .foregroundStyle(AppColors.textMuted) TextField(placeholder, text: text) .font(AppTypography.body) .foregroundStyle(AppColors.textPrimary) .padding(.horizontal, 12) .frame(height: 50) .background(AppColors.backgroundLight) .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 14, style: .continuous) .stroke(AppColors.secondary.opacity(0.2), lineWidth: 1) ) } } } #endif