import SwiftUI #if canImport(LCEssentials) import LCEssentials #endif /// State -> city picker for anonymous browsing (public store locator). /// Replaces AddressesView in the address-picker modal when the user is /// not authenticated — see docs/plans/public-store-locator-sdd.md. struct PublicLocationPickerView: View { @Binding var appState: AppState @Environment(\.dismiss) var dismiss private enum Step { case state case city } @State private var step: Step = .state @State private var locations: PublicLocationsResult = [:] @State private var selectedState: String? = nil @State private var isLoading = false @State private var errorMessage: String? = nil private var states: [String] { locations.keys.sorted() } private var cities: [String] { guard let selectedState else { return [] } return (locations[selectedState] ?? []).sorted() } var body: some View { LCENavigationView { content } .setLeftButton(image: AnyView(AppBackButtonIcon())) { back() } .setTitle(text: Text(step == .state ? "Escolha seu estado" : "Escolha sua cidade").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) .setNavigationBarBackgroundColor(AppColors.backgroundLight) .buttonStyle(.plain) } private var content: some View { ZStack { AppColors.backgroundLight.ignoresSafeArea() VStack(spacing: 20) { if isLoading { ProgressView() .padding(.top, 40) } else if let errorMessage { VStack(spacing: 12) { Text(errorMessage) .font(AppTypography.body) .foregroundStyle(AppColors.textMuted) .multilineTextAlignment(.center) Button("Tentar novamente") { Task { await loadLocations() } } .font(AppTypography.heading3) .foregroundStyle(AppColors.primary) } .padding(.horizontal, 20) .padding(.top, 40) } else { list } Spacer(minLength: 0) } .padding(.top, 18) } .navigationBarBackButtonHidden(true) .appHiddenNavigationBar() .task { await loadLocations() } } private var list: some View { ScrollView(showsIndicators: false) { LazyVStack(spacing: 12) { switch step { case .state: ForEach(states, id: \.self) { state in rowButton(title: state) { selectedState = state step = .city } } case .city: ForEach(cities, id: \.self) { city in rowButton(title: city) { confirmSelection(city: city) } } } } .padding(.horizontal, 20) .padding(.top, 8) } } private func rowButton(title: String, action: @escaping () -> Void) -> some View { Button(action: action) { HStack { Text(title) .font(AppTypography.body) .foregroundStyle(AppColors.textPrimary) Spacer() Image(systemName: "chevron.right") .font(.system(size: 14, weight: .semibold)) .foregroundStyle(AppColors.textMuted) } .padding(.horizontal, 18) .padding(.vertical, 16) .background(AppColors.surface) .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) } .buttonStyle(.plain) } private func back() { switch step { case .city: step = .state errorMessage = nil case .state: dismiss() } } private func confirmSelection(city: String) { guard let selectedState else { return } GuestLocationStore.shared.selectedState = selectedState GuestLocationStore.shared.selectedCity = city appState.address.display = "\(city), \(selectedState)" appState.address.onboardingMessage = nil dismiss() } @MainActor private func loadLocations() async { isLoading = true errorMessage = nil do { locations = try await PublicLocationService.shared.fetchLocations() if locations.isEmpty { errorMessage = "Nenhum estado disponível no momento." } } catch { print("[PublicLocationPickerView] loadLocations failed: \(error)") errorMessage = "Não foi possível carregar. Tente novamente." } isLoading = false } } #Preview { NavigationStack { PublicLocationPickerView(appState: .constant(AppState())) } }