import SwiftUI import ComposeApp // FIX_SKIPPED: LE-2 — This view calls APILayer directly (loadUsers, loadShareCode, // generateShareCode, removeUser). Fixing requires extracting a dedicated ManageUsersViewModel. // Architectural refactor deferred — requires new ViewModel. struct ManageUsersView: View { let residenceId: Int32 let residenceName: String let isPrimaryOwner: Bool let residenceOwnerId: Int32 let residence: ResidenceResponse? @Environment(\.dismiss) private var dismiss @State private var users: [ResidenceUserResponse] = [] private var ownerId: Int32 { residenceOwnerId } @State private var shareCode: ShareCodeResponse? @State private var isLoading = true @State private var errorMessage: String? @State private var isGeneratingCode = false @State private var shareFileURL: URL? @ObservedObject private var sharingManager = ResidenceSharingManager.shared var body: some View { NavigationStack { ZStack { WarmGradientBackground() if isLoading { ProgressView() } else if let error = errorMessage { ErrorView(message: error) { loadUsers() } } else { ScrollView { VStack(spacing: 16) { // Share code section (primary owner only) if isPrimaryOwner { ShareCodeCard( shareCode: shareCode, isGeneratingCode: isGeneratingCode, isGeneratingPackage: sharingManager.isGeneratingPackage, onGenerateCode: generateShareCode, onEasyShare: easyShare ) .padding(.horizontal) .padding(.top) } // Users list VStack(alignment: .leading, spacing: 12) { Text("\(L10n.Residences.users) (\(users.count))") .font(.headline) .padding(.horizontal) ForEach(users, id: \.id) { user in UserListItem( user: user, isOwner: user.id == ownerId, isPrimaryOwner: isPrimaryOwner, onRemove: { removeUser(userId: user.id) } ) } } .padding(.bottom) } } .accessibilityIdentifier("ManageUsers.UsersList") } } .listStyle(.plain) .scrollContentBackground(.hidden) .navigationTitle(L10n.Residences.manageUsers) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button(L10n.Common.close) { dismiss() } } } } .onAppear { shareCode = nil loadUsers() loadShareCode() } .sheet(isPresented: Binding( get: { shareFileURL != nil }, set: { if !$0 { shareFileURL = nil } } )) { if let url = shareFileURL { ShareSheet(activityItems: [url]) } } .alert("Error", isPresented: Binding( get: { sharingManager.errorMessage != nil }, set: { if !$0 { sharingManager.errorMessage = nil } } )) { Button("OK", role: .cancel) {} } message: { Text(sharingManager.errorMessage ?? "Failed to create share link.") } } private func easyShare() { guard let residence = residence else { return } Task { if let url = await sharingManager.createShareableFile(residence: residence) { shareFileURL = url } } } private func loadUsers() { guard TokenStorage.shared.getToken() != nil else { errorMessage = "Not authenticated" return } isLoading = true errorMessage = nil Task { do { let result = try await APILayer.shared.getResidenceUsers(residenceId: Int32(Int(residenceId))) await MainActor.run { if let successResult = result as? ApiResultSuccess, let responseData = successResult.data as? [ResidenceUserResponse] { self.users = responseData self.isLoading = false } else if let errorResult = ApiResultBridge.error(from: result) { self.errorMessage = ErrorMessageParser.parse(errorResult.message) self.isLoading = false } else { self.errorMessage = "Failed to load users" self.isLoading = false } } } catch { await MainActor.run { self.errorMessage = ErrorMessageParser.parse(error.localizedDescription) self.isLoading = false } } } } private func loadShareCode() { guard TokenStorage.shared.getToken() != nil else { return } Task { do { let result = try await APILayer.shared.getShareCode(residenceId: Int32(Int(residenceId))) await MainActor.run { if let successResult = result as? ApiResultSuccess { self.shareCode = successResult.data } // It's okay if there's no active share code } } catch { // It's okay if there's no active share code } } } private func generateShareCode() { guard TokenStorage.shared.getToken() != nil else { return } isGeneratingCode = true Task { do { let result = try await APILayer.shared.generateShareCode(residenceId: Int32(Int(residenceId))) await MainActor.run { if let successResult = result as? ApiResultSuccess, let response = successResult.data { self.shareCode = response.shareCode self.isGeneratingCode = false } else if let errorResult = ApiResultBridge.error(from: result) { self.errorMessage = ErrorMessageParser.parse(errorResult.message) self.isGeneratingCode = false } else { self.errorMessage = "Failed to generate share code" self.isGeneratingCode = false } } } catch { await MainActor.run { self.errorMessage = ErrorMessageParser.parse(error.localizedDescription) self.isGeneratingCode = false } } } } private func removeUser(userId: Int32) { guard TokenStorage.shared.getToken() != nil else { return } Task { do { let result = try await APILayer.shared.removeUser(residenceId: Int32(Int(residenceId)), userId: Int32(Int(userId))) await MainActor.run { if result is ApiResultSuccess { // Remove user from local list self.users.removeAll { $0.id == userId } } else if let errorResult = ApiResultBridge.error(from: result) { self.errorMessage = ErrorMessageParser.parse(errorResult.message) } else { self.errorMessage = "Failed to remove user" } } } catch { await MainActor.run { self.errorMessage = ErrorMessageParser.parse(error.localizedDescription) } } } } } #Preview { ManageUsersView(residenceId: 1, residenceName: "My Home", isPrimaryOwner: true, residenceOwnerId: 1, residence: nil) }