diff --git a/pedi-foods/Sources/PediFoods/Services/ApiCardModels.swift b/pedi-foods/Sources/PediFoods/Services/ApiCardModels.swift index b4a5102..c4c7b94 100644 --- a/pedi-foods/Sources/PediFoods/Services/ApiCardModels.swift +++ b/pedi-foods/Sources/PediFoods/Services/ApiCardModels.swift @@ -65,3 +65,19 @@ struct CreditCardOrderPayload: Encodable { let expiryYear: String let ccv: String } + +struct ChangePaymentMethodPayload: Encodable { + let paymentMethod: String + let clientCpfCnpj: String? + let creditCard: CreditCardOrderPayload? + let creditCardHolderInfo: SaveCardHolderInfoPayload? + let savedCardId: String? +} + +struct ChangePaymentMethodResult: Decodable { + let paymentMethod: String? + let paymentLocation: String? + let paymentId: String? + let paymentPayload: CreateOrderPaymentPayload? + let payment: CreateOrderPaymentInfo? +} diff --git a/pedi-foods/Sources/PediFoods/Services/ApiService.swift b/pedi-foods/Sources/PediFoods/Services/ApiService.swift index 994272f..0d90e42 100644 --- a/pedi-foods/Sources/PediFoods/Services/ApiService.swift +++ b/pedi-foods/Sources/PediFoods/Services/ApiService.swift @@ -451,6 +451,12 @@ final class ApiService { return try await sendEnvelope(req) } + func changePaymentMethod(storeId: String, orderId: String, payload: ChangePaymentMethodPayload) async throws -> ApiEnvelope { + let body = try JSONEncoder().encode(payload) + let req = ApiRequest(path: "/api/store/\(storeId)/orders/\(orderId)/payment-method", method: "PATCH", module: .store, requiresAuth: true, body: body) + return try await sendEnvelope(req) + } + // MARK: - Profile CPF func updateProfileCpf(cpf: String) async throws -> ApiEnvelope { diff --git a/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView+Logic.swift b/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView+Logic.swift index 7de4bf1..55629a6 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView+Logic.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView+Logic.swift @@ -397,9 +397,24 @@ extension CheckoutView { ? pixFromPayment?.expirationDate : pixFromPayload?.expirationDate if let copyPaste, copyPaste.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false { + let itemsData = (try? JSONEncoder().encode(appState.cart.toOrderItemsPayload())).flatMap { String(data: $0, encoding: .utf8) } ?? "[]" + let storeId = appState.cart.storeId ?? "" pixPaymentContext = PixPaymentContext( - id: orderId, orderId: orderId, shortId: result.shortId, - copyPaste: copyPaste, qrCodeImageBase64: qrCodeImage, expirationDate: expirationDate + id: orderId, + orderId: orderId, + shortId: result.shortId, + storeId: storeId, + copyPaste: copyPaste, + qrCodeImageBase64: qrCodeImage, + expirationDate: expirationDate, + total: totalValue, + profileName: appState.profile.name, + profileEmail: appState.profile.email, + profilePhone: appState.profile.phone, + addressZip: selectedCustomerAddress?.zipCode, + addressNumber: selectedCustomerAddress?.number, + deliveryType: deliveryType.rawValue, + itemsJSON: itemsData ) return } diff --git a/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift b/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift index d644a89..9ca15a4 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift @@ -156,6 +156,7 @@ struct CheckoutView: View { .navigationDestination(item: $pixPaymentContext) { context in PaymentPixView( context: context, + appState: $appState, onPaymentConfirmed: { appState.cart.clear() SessionStateStore.clearPendingCartOrder() @@ -560,9 +561,19 @@ struct PixPaymentContext: Identifiable, Hashable { let id: String let orderId: String let shortId: String? + let storeId: String let copyPaste: String let qrCodeImageBase64: String? let expirationDate: String? + // Para troca de pagamento + let total: Double + let profileName: String + let profileEmail: String + let profilePhone: String + let addressZip: String? + let addressNumber: String? + let deliveryType: String + let itemsJSON: String } struct OrderTrackingContext: Identifiable, Hashable { @@ -625,6 +636,7 @@ struct CardPaymentContext: Identifiable, Hashable { struct PaymentPixView: View { let context: PixPaymentContext + @Binding var appState: AppState var onPaymentConfirmed: (() -> Void)? = nil var onOpenTracking: (() -> Void)? = nil @Environment(\.dismiss) var dismiss @@ -633,9 +645,20 @@ struct PaymentPixView: View { @State var hasOpenedTracking = false @State var hasShownPixExpiredSnackbar = false @State var currentTime = Date() + @State var showChangePaymentSheet = false + @State var savedCards: [SavedCard] = [] + @State var currentContext: PixPaymentContext + + init(context: PixPaymentContext, appState: Binding, onPaymentConfirmed: (() -> Void)? = nil, onOpenTracking: (() -> Void)? = nil) { + self.context = context + self._appState = appState + self.onPaymentConfirmed = onPaymentConfirmed + self.onOpenTracking = onOpenTracking + _currentContext = State(initialValue: context) + } private var qrImageSource: String? { - guard let raw = context.qrCodeImageBase64?.trimmingCharacters(in: .whitespacesAndNewlines), + guard let raw = currentContext.qrCodeImageBase64?.trimmingCharacters(in: .whitespacesAndNewlines), raw.isEmpty == false else { return nil } if raw.lowercased().hasPrefix("data:image") { return raw } return "data:image/png;base64,\(raw)" @@ -683,33 +706,53 @@ struct PaymentPixView: View { .frame(height: 20) .overlay( VStack(spacing: 8) { - Text(context.copyPaste) + Text(currentContext.copyPaste) .font(.system(size: 12, weight: .medium, design: .monospaced)) .foregroundStyle(AppColors.textMuted) .lineLimit(0) .multilineTextAlignment(.center) .padding(.horizontal, 10) - + } .padding(.vertical, 14) ) - - if let expirationDate = context.expirationDate, expirationDate.isEmpty == false { + + if let expirationDate = currentContext.expirationDate, expirationDate.isEmpty == false { Text(expirationLabel) .font(.caption) .foregroundStyle(isPixExpired ? Color.red : AppColors.textMuted) } - - PrimaryButton(title: "Copiar Código PIX") { - if isPixExpired { - showPixExpiredSnackbar() - return + + HStack(spacing: 10) { + PrimaryButton(title: "Copiar Código PIX") { + if isPixExpired { + showPixExpiredSnackbar() + return + } + copyToClipboard(currentContext.copyPaste) + SnackbarCenter.shared.show(title: "Código PIX copiado.", style: .success, icon: "doc.on.doc.fill", duration: 2.0) } - copyToClipboard(context.copyPaste) - SnackbarCenter.shared.show(title: "Código PIX copiado.", style: .success, icon: "doc.on.doc.fill", duration: 2.0) + .disabled(isPixExpired) + .opacity(isPixExpired ? 0.5 : 1.0) + + Button { + Task { await loadSavedCards() } + showChangePaymentSheet = true + } label: { + HStack(spacing: 6) { + Text("Trocar") + .font(AppTypography.heading3) + Image(systemName: "chevron.down") + .font(.system(size: 12, weight: .semibold)) + } + .foregroundStyle(AppColors.textPrimary) + .frame(height: 54) + .padding(.horizontal, 16) + .background(AppColors.surface) + .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)) + } + .buttonStyle(.plain) } - .disabled(isPixExpired) - .opacity(isPixExpired ? 0.5 : 1.0) .padding(.top, 10) } .padding(20) @@ -717,6 +760,26 @@ struct PaymentPixView: View { .background(AppColors.backgroundLight) .navigationTitle("Pagamento via PIX") .appInlineNavigationTitle() + .sheet(isPresented: $showChangePaymentSheet) { + ChangePaymentSheet( + pixContext: currentContext, + savedCards: savedCards, + appState: $appState, + onChanged: { newContext in + showChangePaymentSheet = false + if let newContext { + currentContext = newContext + tracker.stop() + tracker.start(orderId: newContext.orderId, jwt: DefaultTokenStore().jwt) + } + }, + onConfirmed: { + showChangePaymentSheet = false + onPaymentConfirmed?() + openTrackingOnce() + } + ) + } .task { tracker.onOrderUpdated = { updated in latestOrder = updated @@ -725,7 +788,7 @@ struct PaymentPixView: View { openTrackingOnce() } } - tracker.start(orderId: context.orderId, jwt: DefaultTokenStore().jwt) + tracker.start(orderId: currentContext.orderId, jwt: DefaultTokenStore().jwt) } .task { while Task.isCancelled == false { @@ -752,8 +815,16 @@ struct PaymentPixView: View { appWriteClipboardText(value) } + @MainActor + private func loadSavedCards() async { + guard savedCards.isEmpty else { return } + if let cards = try? await ApiService().listCards().result { + savedCards = cards + } + } + private var parsedExpirationDate: Date? { - let raw = (context.expirationDate ?? "").trimmingCharacters(in: .whitespacesAndNewlines) + let raw = (currentContext.expirationDate ?? "").trimmingCharacters(in: .whitespacesAndNewlines) guard raw.isEmpty == false else { return nil } let iso = ISO8601DateFormatter() @@ -1257,3 +1328,347 @@ struct PaymentCardView: View { String(format: "R$ %.2f", value).replacingOccurrences(of: ".", with: ",") } } + +// MARK: - ChangePaymentSheet + +struct ChangePaymentSheet: View { + let pixContext: PixPaymentContext + let savedCards: [SavedCard] + @Binding var appState: AppState + let onChanged: (PixPaymentContext?) -> Void + let onConfirmed: () -> Void + + @Environment(\.dismiss) var dismiss + @State var isSubmitting = false + @State var showCardSheet = false + + var body: some View { + VStack(spacing: 0) { + HStack { + Text("Trocar Pagamento") + .font(AppTypography.heading2) + .foregroundStyle(AppColors.textPrimary) + Spacer() + Button { dismiss() } label: { + Image(systemName: "xmark") + .font(.system(size: 12, weight: .bold)) + .foregroundStyle(AppColors.textMuted) + .frame(width: 30, height: 30) + .background(AppColors.surface) + .clipShape(Circle()) + } + .buttonStyle(.plain) + } + .padding(.horizontal, 20) + .padding(.top, 20) + .padding(.bottom, 16) + + ScrollView(showsIndicators: false) { + VStack(spacing: 12) { + optionRow(icon: "qrcode", title: "Novo QR Code PIX", subtitle: "Gera um novo código PIX") { + Task { await changeToMethod("PIX") } + } + optionRow(icon: "creditcard.fill", title: "Cartão de Crédito", subtitle: "Débito imediato no cartão") { + showCardSheet = true + } + optionRow(icon: "banknote.fill", title: "Pagar na Entrega", subtitle: "Pague ao receber o pedido") { + Task { await changeToMethod("CASH") } + } + } + .padding(.horizontal, 20) + .padding(.bottom, 32) + } + } + .background(AppColors.backgroundLight) + .sheet(isPresented: $showCardSheet) { + let items = (try? JSONDecoder().decode([CreateOrderItemPayload].self, from: pixContext.itemsJSON.data(using: .utf8) ?? Data())) ?? [] + let cardCtx = CardPaymentContext.build(storeId: pixContext.storeId, total: pixContext.total, deliveryType: pixContext.deliveryType, profile: appState.profile, address: nil, items: items) + ChangePaymentCardSheet(pixContext: pixContext, cardContext: cardCtx, savedCards: savedCards, appState: $appState) { _, _ in + showCardSheet = false + onConfirmed() + } + } + } + + private func optionRow(icon: String, title: String, subtitle: String, action: @escaping () -> Void) -> some View { + Button(action: action) { + HStack(spacing: 14) { + Image(systemName: icon).font(.system(size: 20)).foregroundStyle(AppColors.primary) + .frame(width: 44, height: 44).background(AppColors.brandSoft) + .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) + VStack(alignment: .leading, spacing: 3) { + Text(title).font(AppTypography.heading3).foregroundStyle(AppColors.textPrimary) + Text(subtitle).font(.caption).foregroundStyle(AppColors.textMuted) + } + Spacer() + if isSubmitting { ProgressView().scaleEffect(0.8) } + else { Image(systemName: "chevron.right").font(.system(size: 12, weight: .semibold)).foregroundStyle(AppColors.textMuted) } + } + .padding(14).background(AppColors.surface) + .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)) + } + .buttonStyle(.plain).disabled(isSubmitting) + } + + @MainActor + private func changeToMethod(_ method: String) async { + isSubmitting = true + defer { isSubmitting = false } + let payload = ChangePaymentMethodPayload(paymentMethod: method, clientCpfCnpj: nil, creditCard: nil, creditCardHolderInfo: nil, savedCardId: nil) + do { + let response = try await ApiService().changePaymentMethod(storeId: pixContext.storeId, orderId: pixContext.orderId, payload: payload) + if response.error { + SnackbarCenter.shared.show(title: response.message ?? "Não foi possível trocar o pagamento.", style: .error, icon: "xmark.octagon.fill", duration: 3.5) + return + } + guard let result = response.result else { return } + if method == "CASH" { + SnackbarCenter.shared.show(title: "Pagamento alterado. Pague ao receber.", style: .success, icon: "checkmark.circle.fill", duration: 3.0) + onConfirmed(); return + } + let pix = result.payment?.pix ?? result.paymentPayload + if let copyPaste = pix?.copyPaste, copyPaste.isEmpty == false { + let newCtx = PixPaymentContext(id: UUID().uuidString, orderId: pixContext.orderId, shortId: pixContext.shortId, storeId: pixContext.storeId, copyPaste: copyPaste, qrCodeImageBase64: pix?.qrCodeImage, expirationDate: pix?.expirationDate, total: pixContext.total, profileName: pixContext.profileName, profileEmail: pixContext.profileEmail, profilePhone: pixContext.profilePhone, addressZip: pixContext.addressZip, addressNumber: pixContext.addressNumber, deliveryType: pixContext.deliveryType, itemsJSON: pixContext.itemsJSON) + SnackbarCenter.shared.show(title: "Novo QR Code gerado.", style: .success, icon: "qrcode", duration: 2.5) + onChanged(newCtx) + } + } catch let error as NetworkError { + let msg: String + if case .httpError(_, let m) = error, let m, m.isEmpty == false { msg = m } else { msg = error.errorDescription ?? "Erro ao trocar pagamento." } + SnackbarCenter.shared.show(title: msg, style: .error, icon: "xmark.octagon.fill", duration: 4.0) + } catch { + SnackbarCenter.shared.show(title: "Erro ao trocar pagamento.", style: .error, icon: "xmark.octagon.fill", duration: 3.0) + } + } +} + +// MARK: - ChangePaymentCardSheet + +struct ChangePaymentCardSheet: View { + let pixContext: PixPaymentContext + let cardContext: CardPaymentContext + let savedCards: [SavedCard] + @Binding var appState: AppState + let onConfirmed: (String, String?) -> Void + + @Environment(\.dismiss) var dismiss + @State var selectedCardId: String? + @State var showNewCardForm = false + @State var isSubmitting = false + + init(pixContext: PixPaymentContext, cardContext: CardPaymentContext, savedCards: [SavedCard], appState: Binding, onConfirmed: @escaping (String, String?) -> Void) { + self.pixContext = pixContext; self.cardContext = cardContext; self.savedCards = savedCards + self._appState = appState; self.onConfirmed = onConfirmed + _selectedCardId = State(initialValue: savedCards.first(where: { $0.isDefault })?.id ?? savedCards.first?.id) + } + + var body: some View { + VStack(spacing: 0) { + HStack { + Text("Cartão de Crédito").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary) + Spacer() + Button { dismiss() } label: { + Image(systemName: "xmark").font(.system(size: 12, weight: .bold)).foregroundStyle(AppColors.textMuted) + .frame(width: 30, height: 30).background(AppColors.surface).clipShape(Circle()) + }.buttonStyle(.plain) + } + .padding(.horizontal, 20).padding(.top, 20).padding(.bottom, 16) + + ScrollView(showsIndicators: false) { + VStack(spacing: 16) { + if savedCards.isEmpty == false { + VStack(spacing: 0) { + ForEach(savedCards) { card in + Button { selectedCardId = card.id } label: { + HStack(spacing: 12) { + Image(systemName: "creditcard.fill").font(.system(size: 18)).foregroundStyle(AppColors.textPrimary) + .frame(width: 44, height: 44).background(AppColors.backgroundLight) + .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) + VStack(alignment: .leading, spacing: 3) { + Text(card.displayLabel).font(AppTypography.heading3).foregroundStyle(AppColors.textPrimary) + Text("Vence \(card.expiryLabel)").font(.caption).foregroundStyle(AppColors.textMuted) + } + Spacer() + Circle().stroke(selectedCardId == card.id ? AppColors.tertiary : AppColors.textMuted.opacity(0.45), lineWidth: 2) + .frame(width: 22, height: 22).background(Circle().fill(selectedCardId == card.id ? AppColors.tertiary : Color.clear)) + }.padding(14) + }.buttonStyle(.plain) + if card.id != savedCards.last?.id { Divider().padding(.horizontal, 14) } + } + } + .background(AppColors.surface) + .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)) + + if let selectedCardId { + PrimaryButton(title: isSubmitting ? "Processando..." : "Pagar com este Cartão") { + guard isSubmitting == false else { return } + Task { await changeToSavedCard(cardId: selectedCardId) } + }.disabled(isSubmitting) + } + Text("ou").font(AppTypography.caption).foregroundStyle(AppColors.textMuted).frame(maxWidth: .infinity, alignment: .center) + } + Button { showNewCardForm = true } label: { + HStack(spacing: 10) { + Image(systemName: "plus.circle.fill").font(.system(size: 18)).foregroundStyle(AppColors.primary) + Text("Novo cartão").font(AppTypography.heading3).foregroundStyle(AppColors.primary) + Spacer() + } + .padding(14).background(AppColors.surface) + .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)) + }.buttonStyle(.plain) + } + .padding(.horizontal, 20).padding(.bottom, 32) + } + } + .background(AppColors.backgroundLight) + .sheet(isPresented: $showNewCardForm) { + ChangePaymentNewCardView(pixContext: pixContext, appState: $appState) { orderId, shortId in + showNewCardForm = false; onConfirmed(orderId, shortId) + } + } + } + + @MainActor + private func changeToSavedCard(cardId: String) async { + isSubmitting = true; defer { isSubmitting = false } + let payload = ChangePaymentMethodPayload(paymentMethod: "CREDIT_CARD", clientCpfCnpj: nil, creditCard: nil, creditCardHolderInfo: nil, savedCardId: cardId) + do { + let response = try await ApiService().changePaymentMethod(storeId: pixContext.storeId, orderId: pixContext.orderId, payload: payload) + if response.error { SnackbarCenter.shared.show(title: response.message ?? "Pagamento recusado.", style: .error, icon: "xmark.octagon.fill", duration: 3.5); return } + onConfirmed(pixContext.orderId, pixContext.shortId) + } catch let error as NetworkError { + let msg: String + if case .httpError(_, let m) = error, let m, m.isEmpty == false { msg = m } else { msg = "Pagamento recusado." } + SnackbarCenter.shared.show(title: msg, style: .error, icon: "xmark.octagon.fill", duration: 4.0) + } catch { SnackbarCenter.shared.show(title: "Erro ao processar.", style: .error, icon: "xmark.octagon.fill", duration: 3.0) } + } +} + +// MARK: - ChangePaymentNewCardView + +struct ChangePaymentNewCardView: View { + let pixContext: PixPaymentContext + @Binding var appState: AppState + let onConfirmed: (String, String?) -> Void + + @Environment(\.dismiss) var dismiss + @State var holderName: String + @State var cardNumber = "" + @State var expiry = "" + @State var cvv = "" + @State var cpf: String + @State var saveCard = false + @State var nickname = "" + @State var isSubmitting = false + + init(pixContext: PixPaymentContext, appState: Binding, onConfirmed: @escaping (String, String?) -> Void) { + self.pixContext = pixContext; self._appState = appState; self.onConfirmed = onConfirmed + _holderName = State(initialValue: appState.wrappedValue.profile.name) + _cpf = State(initialValue: appState.wrappedValue.profile.cpf) + } + + var canSubmit: Bool { + holderName.trimmingCharacters(in: .whitespaces).isEmpty == false && + cardNumber.filter(\.isNumber).count >= 13 && + expiry.filter(\.isNumber).count == 6 && + cvv.filter(\.isNumber).count >= 3 && + cpf.filter(\.isNumber).count == 11 + } + + var body: some View { + VStack(spacing: 0) { + HStack { + Text("Novo Cartão").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary) + Spacer() + Button { dismiss() } label: { + Image(systemName: "xmark").font(.system(size: 12, weight: .bold)).foregroundStyle(AppColors.textMuted) + .frame(width: 30, height: 30).background(AppColors.surface).clipShape(Circle()) + }.buttonStyle(.plain) + } + .padding(.horizontal, 20).padding(.top, 20).padding(.bottom, 16) + + ScrollView(showsIndicators: false) { + VStack(spacing: 16) { + VStack(spacing: 10) { + cardField("Número do Cartão", placeholder: "0000 0000 0000 0000", text: $cardNumber).keyboardType(.numberPad) + .onChange(of: cardNumber) { _, v in + let d = String(v.filter(\.isNumber).prefix(16)) + let m = stride(from: 0, to: d.count, by: 4).map { i -> String in let s = d.index(d.startIndex, offsetBy: i); let e = d.index(s, offsetBy: min(4, d.count - i)); return String(d[s.. { do { _ = try await ApiService().updateProfileCpf(cpf: cleanCpf) } catch {} } + } + if saveCard { + let sp = SaveCardPayload(creditCard: SaveCardCreditCardPayload(holderName: holderName, number: cleanNumber, expiryMonth: expiryMonth, expiryYear: expiryYear, ccv: cvv), creditCardHolderInfo: holderInfo, nickname: nickname.trimmingCharacters(in: .whitespaces).isEmpty ? nil : nickname, isDefault: false) + Task { do { _ = try await ApiService().saveCard(payload: sp) } catch {} } + } + + do { + let response = try await ApiService().changePaymentMethod(storeId: pixContext.storeId, orderId: pixContext.orderId, payload: payload) + if response.error { SnackbarCenter.shared.show(title: response.message ?? "Pagamento recusado.", style: .error, icon: "xmark.octagon.fill", duration: 4.0); return } + onConfirmed(pixContext.orderId, pixContext.shortId) + } catch let error as NetworkError { + let msg: String + if case .httpError(_, let m) = error, let m, m.isEmpty == false { msg = m } else { msg = error.errorDescription ?? "Erro ao processar." } + SnackbarCenter.shared.show(title: msg, style: .error, icon: "xmark.octagon.fill", duration: 5.0) + } catch { SnackbarCenter.shared.show(title: "Erro ao processar pagamento.", style: .error, icon: "xmark.octagon.fill", duration: 3.0) } + } + + private func cardField(_ label: String, placeholder: String, text: Binding) -> some View { + VStack(alignment: .leading, spacing: 6) { + Text(label).font(AppTypography.caption).foregroundStyle(AppColors.textMuted) + TextField(placeholder, text: text).appNoAutoCap() + .padding(.horizontal, 12).frame(height: 46).background(AppColors.backgroundLight) + .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) + } + } +} diff --git a/pedi-foods/Sources/PediFoods/Views/Main/OrdersView.swift b/pedi-foods/Sources/PediFoods/Views/Main/OrdersView.swift index ee664a9..bf1c9ec 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/OrdersView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/OrdersView.swift @@ -1,6 +1,7 @@ import SwiftUI struct OrdersView: View { + @Binding var appState: AppState @Environment(\.dismiss) var dismiss @State var isLoading = false @State var errorMessage: String? = nil @@ -58,7 +59,8 @@ struct OrdersView: View { initialShortId: context.shortId, fallbackPaymentMethod: context.paymentMethod, fallbackTotal: context.total, - routeIntent: context.intent + routeIntent: context.intent, + appState: $appState ) } } @@ -629,6 +631,7 @@ struct OrderEntryDestinationView: View { let fallbackPaymentMethod: String? let fallbackTotal: Double? let routeIntent: OrderRouteIntent + @Binding var appState: AppState @State var isResolvingRoute = true @State var didResolve = false @@ -650,6 +653,7 @@ struct OrderEntryDestinationView: View { } else if let pixContext { PaymentPixView( context: pixContext, + appState: $appState, onPaymentConfirmed: { orderTrackingContext = OrderTrackingContext(orderId: pixContext.orderId, shortId: pixContext.shortId) }, @@ -701,15 +705,25 @@ struct OrderEntryDestinationView: View { let expirationDate = (pixFromPayment?.expirationDate?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false) ? pixFromPayment?.expirationDate : pixFromPayload?.expirationDate + let storeId = order.storeId ?? "" pixContext = PixPaymentContext( id: order.id, orderId: order.id, shortId: order.shortId ?? initialShortId, + storeId: storeId, copyPaste: copyPaste?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false ? (copyPaste ?? "") : "Código PIX indisponível no momento. Aguarde e tente novamente.", qrCodeImageBase64: qrCodeImage, - expirationDate: expirationDate + expirationDate: expirationDate, + total: order.total ?? 0, + profileName: "", + profileEmail: "", + profilePhone: "", + addressZip: nil, + addressNumber: nil, + deliveryType: order.deliveryType ?? "DELIVERY", + itemsJSON: "[]" ) return } diff --git a/pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift b/pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift index 495ddc8..4aa2b04 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift @@ -28,7 +28,7 @@ struct ProfileView: View { VStack(spacing: 14) { NavigationLink { - OrdersView() + OrdersView(appState: $appState) } label: { ProfileMenuRow(icon: "list.bullet.clipboard.fill", title: "Meus Pedidos") }