import Foundation import SwiftUI extension HomeView { func buildCategories(from stores: [StoreSummary]) -> [CategoryModel] { var unique: [CategoryModel] = [ .init(id: "all", title: "Todas", systemIcon: "line.3.horizontal.decrease.circle", emojiIcon: nil) ] var seen = Set() for store in stores { let raw = (store.category ?? "").trimmingCharacters(in: .whitespacesAndNewlines) if raw.isEmpty { continue } let dedupe = raw.lowercased() if seen.contains(dedupe) { continue } seen.insert(dedupe) unique.append(.init(id: raw, title: raw, systemIcon: categoryIcon(for: raw), emojiIcon: nil)) } return unique } @MainActor func loadHomeCategories(withFallbackStores stores: [StoreSummary]) async { do { let response = try await ApiService().listPublicCategories() if response.error == false, let remote = response.result, remote.isEmpty == false { categories = mapPublicCategories(remote) return } } catch { // Fallback handled below. } categories = buildCategories(from: stores) } func mapPublicCategories(_ remote: [PublicCategory]) -> [CategoryModel] { var mapped: [CategoryModel] = [] var seen = Set() for item in remote { let id = item.id.trimmingCharacters(in: .whitespacesAndNewlines) let title = item.name.trimmingCharacters(in: .whitespacesAndNewlines) if id.isEmpty || title.isEmpty { continue } if seen.contains(id.lowercased()) { continue } seen.insert(id.lowercased()) mapped.append( .init( id: id, title: title, systemIcon: id.lowercased() == "all" ? "line.3.horizontal.decrease.circle" : nil, emojiIcon: item.icon ) ) } if mapped.contains(where: { $0.id.lowercased() == "all" }) == false { mapped.insert(.init(id: "all", title: "Todas", systemIcon: "line.3.horizontal.decrease.circle", emojiIcon: nil), at: 0) } else { mapped.sort { lhs, rhs in if lhs.id.lowercased() == "all" { return true } if rhs.id.lowercased() == "all" { return false } return lhs.title < rhs.title } } return mapped } func categoryIcon(for category: String) -> String { let value = category.folding(options: [.diacriticInsensitive, .caseInsensitive], locale: .current).lowercased() if value.contains("pizza") { return "takeoutbag.and.cup.and.straw" } if value.contains("lanche") || value.contains("hamburg") || value.contains("burger") { return "fork.knife" } if value.contains("cafe") || value.contains("breakfast") { return "sun.max" } if value.contains("doce") || value.contains("sobremesa") || value.contains("dessert") { return "cup.and.saucer" } return "storefront" } @MainActor func resolveCoordinates(forceRefresh: Bool) async -> (Double, Double)? { if !forceRefresh { if let lat = appState.address.latitude, let lng = appState.address.longitude { return (lat, lng) } if let cached = LocationService.shared.cachedLocation() { appState.address.latitude = cached.0 appState.address.longitude = cached.1 return cached } if hasConfiguredAddress() == false { return nil } } let coordinate = await LocationService.shared.requestLocationAsync(timeoutSeconds: 3) if let coordinate { appState.address.latitude = coordinate.0 appState.address.longitude = coordinate.1 } return coordinate } func hasConfiguredAddress() -> Bool { if appState.address.selectedId != nil { return true } if appState.address.latitude != nil, appState.address.longitude != nil { return true } let normalized = appState.address.display .trimmingCharacters(in: .whitespacesAndNewlines) .lowercased() return normalized.isEmpty == false && normalized != "defina seu endereco" } func storesUserMessage(_ error: Error) -> String { if let service = error as? ApiServiceError { return service.errorDescription ?? "Não foi possível carregar os estabelecimentos." } if let network = error as? NetworkError { return network.errorDescription ?? "Não foi possível carregar os estabelecimentos." } return "Não foi possível carregar os estabelecimentos." } }