Fix login

This commit is contained in:
Daniel Arantes Loverde
2026-02-21 18:51:39 -03:00
parent 0f5ad4c268
commit 3a3dc7217b
17 changed files with 1296 additions and 91 deletions

View File

@@ -0,0 +1,205 @@
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
}
}