import SwiftUI struct OrdersView: View { @State var isLoading = false @State var errorMessage: String? = nil @State var orders: [AppOrderSummary] = [] var body: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 14) { Text("Histórico de Pedidos") .font(AppTypography.heading1) .foregroundStyle(AppColors.textPrimary) .padding(.top, 6) if isLoading { ProgressView() .frame(maxWidth: .infinity) .padding(.top, 24) } else if let errorMessage, errorMessage.isEmpty == false { Text(errorMessage) .font(AppTypography.body) .foregroundStyle(Color.red) .multilineTextAlignment(.center) .frame(maxWidth: .infinity) .padding(.top, 24) } else if orders.isEmpty { Text("Nenhum pedido encontrado.") .font(AppTypography.body) .foregroundStyle(AppColors.textMuted) .padding(.top, 24) } else { ForEach(orders) { order in NavigationLink { OrderTrackingView(orderId: order.id, initialShortId: order.shortId) } label: { orderRow(order) } .buttonStyle(.plain) } } } .padding(.horizontal, 20) .padding(.bottom, UIDevice.bottomNotch + 18) } .background(AppColors.backgroundLight) .navigationTitle("Meus Pedidos") .navigationBarTitleDisplayMode(.inline) .task { await loadOrders() } } private func orderRow(_ order: AppOrderSummary) -> some View { HStack(spacing: 12) { RoundedRectangle(cornerRadius: 10, style: .continuous) .fill(AppColors.brandSoft) .frame(width: 44, height: 44) .overlay( Image(systemName: "bag.fill") .foregroundStyle(AppColors.primary) ) VStack(alignment: .leading, spacing: 4) { Text("Pedido #\(order.shortId ?? order.id)") .font(AppTypography.heading3) .foregroundStyle(AppColors.textPrimary) Text(humanReadableStatus(order.status ?? order.paymentStatus ?? "Pendente")) .font(AppTypography.caption) .foregroundStyle(AppColors.textMuted) } Spacer() VStack(alignment: .trailing, spacing: 4) { Text(formatCurrency(order.total ?? 0)) .font(AppTypography.heading3) .foregroundStyle(AppColors.textPrimary) if let createdAt = formatDate(order.createdAt) { Text(createdAt) .font(AppTypography.caption) .foregroundStyle(AppColors.textMuted) } } } .padding(14) .background(AppColors.surface) .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)) } private func humanReadableStatus(_ raw: String) -> String { let normalized = raw.uppercased() if normalized.contains("PAYMENT_PENDING") || normalized == "PENDING" { return "Aguardando pagamento" } if normalized.contains("CONFIRMED") { return "Confirmado" } if normalized.contains("OUT_FOR_DELIVERY") || normalized.contains("ROTA") { return "Em rota" } if normalized.contains("COMPLETED") || normalized.contains("DELIVERED") { return "Entregue" } if normalized.contains("CANCEL") { return "Cancelado" } return raw.capitalized } private func formatCurrency(_ value: Double) -> String { String(format: "R$ %.2f", value).replacingOccurrences(of: ".", with: ",") } private func formatDate(_ isoValue: String?) -> String? { guard let isoValue, isoValue.isEmpty == false else { return nil } let iso = ISO8601DateFormatter() iso.formatOptions = [.withInternetDateTime, .withFractionalSeconds] var date = iso.date(from: isoValue) if date == nil { iso.formatOptions = [.withInternetDateTime] date = iso.date(from: isoValue) } guard let date else { return nil } let formatter = DateFormatter() formatter.locale = Locale(identifier: "pt_BR") formatter.dateFormat = "dd/MM HH:mm" return formatter.string(from: date) } @MainActor private func loadOrders() async { isLoading = true errorMessage = nil let cachedTracked = SessionStateStore.loadTrackedOrders() if cachedTracked.isEmpty == false { let mapped = cachedTracked.map { AppOrderSummary.fromTracked($0) } orders = mergeOrders(apiOrders: orders, trackedOrders: mapped) } do { let response = try await ApiService().listOrders() if response.error { errorMessage = response.message ?? "Não foi possível carregar os pedidos." } else { let remote = response.result ?? [] orders = mergeOrders(apiOrders: remote, trackedOrders: orders) } } catch { if orders.isEmpty { errorMessage = "Não foi possível carregar os pedidos." } } isLoading = false } private func mergeOrders(apiOrders: [AppOrderSummary], trackedOrders: [AppOrderSummary]) -> [AppOrderSummary] { var map: [String: AppOrderSummary] = [:] for item in trackedOrders { map[item.id] = item } for item in apiOrders { map[item.id] = item } return map.values.sorted { lhs, rhs in let left = lhs.updatedAt ?? lhs.createdAt ?? "" let right = rhs.updatedAt ?? rhs.createdAt ?? "" return left > right } } } extension AppOrderSummary { static func fromTracked(_ tracked: PublicOrderResult) -> AppOrderSummary { AppOrderSummary( id: tracked.id, shortId: tracked.shortId, total: tracked.total, status: tracked.status, paymentStatus: tracked.paymentStatus, paymentMethod: tracked.paymentMethod, deliveryType: tracked.deliveryType, storeName: tracked.storeName, createdAt: tracked.createdAt, updatedAt: tracked.updatedAt ) } init( id: String, shortId: String?, total: Double?, status: String?, paymentStatus: String?, paymentMethod: String?, deliveryType: String?, storeName: String?, createdAt: String?, updatedAt: String? ) { self.id = id self.shortId = shortId self.total = total self.status = status self.paymentStatus = paymentStatus self.paymentMethod = paymentMethod self.deliveryType = deliveryType self.storeName = storeName self.createdAt = createdAt self.updatedAt = updatedAt } }