import SwiftUI struct BusinessSelectionScreen: View { @EnvironmentObject var appState: AppState @State private var businesses: [Employment] = [] @State private var isLoading = true @State private var error: String? var body: some View { NavigationStack { Group { if isLoading { ProgressView("Loading businesses...") .frame(maxWidth: .infinity, maxHeight: .infinity) } else if let error = error { VStack(spacing: 16) { Image(systemName: "exclamationmark.triangle") .font(.largeTitle) .foregroundColor(.orange) Text(error) .foregroundColor(.secondary) .multilineTextAlignment(.center) Button("Retry") { loadBusinesses() } .buttonStyle(.borderedProminent) .tint(.payfritGreen) } .padding() } else if businesses.isEmpty { VStack(spacing: 16) { Image(systemName: "building.2") .font(.largeTitle) .foregroundColor(.secondary) Text("No businesses found") .foregroundColor(.secondary) } } else { ScrollView { LazyVStack(spacing: 12) { ForEach(businesses) { biz in NavigationLink(value: biz) { VStack(spacing: 0) { BusinessHeaderImage(businessId: biz.businessId) HStack { VStack(alignment: .leading, spacing: 2) { Text(biz.businessName) .font(.subheadline.weight(.semibold)) .foregroundColor(.primary) if !biz.businessCity.isEmpty { Text(biz.businessCity) .font(.caption) .foregroundColor(.secondary) } } Spacer() Image(systemName: "chevron.right") .font(.caption) .foregroundColor(.secondary) } .padding(.horizontal, 12) .padding(.vertical, 8) } .background(Color(.systemBackground)) .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 12, style: .continuous) .stroke(Color(.systemGray4), lineWidth: 0.5) ) .shadow(color: .black.opacity(0.06), radius: 4, x: 0, y: 2) } .buttonStyle(.plain) } } .padding(.horizontal, 16) .padding(.top, 8) .padding(.bottom, 20) } } } .navigationTitle("Select Business") .navigationDestination(for: Employment.self) { biz in BeaconDashboard(business: biz) } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { logout() } label: { Image(systemName: "rectangle.portrait.and.arrow.right") } } } } .task { loadBusinesses() } } private func loadBusinesses() { isLoading = true error = nil Task { do { businesses = try await APIService.shared.getMyBusinesses() isLoading = false } catch let apiError as APIError where apiError == .unauthorized { await appState.handleUnauthorized() } catch { self.error = error.localizedDescription isLoading = false } } } private func logout() { Task { await AuthStorage.shared.clearAuth() await APIService.shared.logout() appState.clearAuth() } } } // Make Employment Hashable for NavigationLink extension Employment: Hashable { static func == (lhs: Employment, rhs: Employment) -> Bool { lhs.employeeId == rhs.employeeId && lhs.businessId == rhs.businessId } func hash(into hasher: inout Hasher) { hasher.combine(employeeId) hasher.combine(businessId) } } // MARK: - Business Header Image struct BusinessHeaderImage: View { let businessId: Int @State private var loadedImage: UIImage? @State private var isLoading = true private var imageURLs: [URL] { [ "https://dev.payfrit.com/uploads/headers/\(businessId).png", "https://dev.payfrit.com/uploads/headers/\(businessId).jpg", ].compactMap { URL(string: $0) } } var body: some View { ZStack { Color(.systemGray6) if let image = loadedImage { Image(uiImage: image) .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: .infinity) } else if isLoading { ProgressView() .tint(.payfritGreen) .frame(maxWidth: .infinity) .frame(height: 100) } else { Image(systemName: "building.2") .font(.system(size: 30)) .foregroundColor(.secondary) .frame(maxWidth: .infinity) .frame(height: 100) } } .task { await loadImage() } } private func loadImage() async { for url in imageURLs { do { let (data, response) = try await URLSession.shared.data(from: url) if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let image = UIImage(data: data) { await MainActor.run { loadedImage = image isLoading = false } return } } catch { continue } } await MainActor.run { isLoading = false } } }