diff --git a/PediFoods.xcodeproj/project.pbxproj b/PediFoods.xcodeproj/project.pbxproj index 3a090eb..76b25cd 100644 --- a/PediFoods.xcodeproj/project.pbxproj +++ b/PediFoods.xcodeproj/project.pbxproj @@ -114,6 +114,7 @@ CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC381D02A37312BE360024F /* Buttons.swift */; }; CD96980F3D7B603126FDE6AC /* URLProtocolStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51798A414469C911D44F0F6 /* URLProtocolStub.swift */; }; CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */; }; + 38452FD60CBDE248531DA484 /* AppBackButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EEF6262A813BB8274266C8 /* AppBackButtonIcon.swift */; }; D2FB3C624B3C27E59BCA997A /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA34EE194C71755B3C82F30 /* MainTabView.swift */; }; D3EC6E0A68B809F7A3370B1B /* ProductDetailSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553A05B9EAD0B9D0D9F1B369 /* ProductDetailSheet.swift */; }; D48148B155BA2F76C41C0787 /* HomeFiltersStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FBD7D809ACE1840165A9DCC /* HomeFiltersStateTests.swift */; }; @@ -299,6 +300,7 @@ EB95D6804877D2ED244811D0 /* Inputs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Inputs.swift; sourceTree = ""; }; ECBFCE20C54A79DEA8F4512B /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedRemoteImage.swift; sourceTree = ""; }; + 52EEF6262A813BB8274266C8 /* AppBackButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppBackButtonIcon.swift; sourceTree = ""; }; F0B079DFAB606DCA87A536C9 /* LaunchSplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchSplashView.swift; sourceTree = ""; }; F18F768F7A2A47111A85FE88 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; F2B2C5BBDFF4473C472F45B4 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; @@ -558,6 +560,7 @@ children = ( 6EC381D02A37312BE360024F /* Buttons.swift */, EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */, + 52EEF6262A813BB8274266C8 /* AppBackButtonIcon.swift */, EB95D6804877D2ED244811D0 /* Inputs.swift */, C4745C12F14B695C77DFE178 /* SnackbarOverlay.swift */, 31916C1A4005396ADA5FF3BC /* StoreCard.swift */, @@ -779,6 +782,7 @@ 3689239282AADF06783CBBEA /* AuthFormatters.swift in Sources */, CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */, CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */, + 38452FD60CBDE248531DA484 /* AppBackButtonIcon.swift in Sources */, DE499D6150C15EB046AF7963 /* CartView.swift in Sources */, 70EA850C067AB6C6B87539E2 /* CheckoutTypes.swift in Sources */, 41BE548D929D3814A736FA12 /* CheckoutView+Logic.swift in Sources */, diff --git a/PediFoods/Components/AppBackButtonIcon.swift b/PediFoods/Components/AppBackButtonIcon.swift new file mode 100644 index 0000000..c84869a --- /dev/null +++ b/PediFoods/Components/AppBackButtonIcon.swift @@ -0,0 +1,13 @@ +import SwiftUI + +struct AppBackButtonIcon: View { + var body: some View { + Image(systemName: "chevron.left") + .font(.system(size: 14, weight: .semibold)) + .foregroundStyle(AppColors.textPrimary) + .frame(width: 32, height: 32) + .background(AppColors.surface) + .clipShape(Circle()) + .shadow(color: .black.opacity(0.06), radius: 8, y: 2) + } +} diff --git a/PediFoods/Views/Auth/LegalViews.swift b/PediFoods/Views/Auth/LegalViews.swift index 58e53b4..ef568b3 100644 --- a/PediFoods/Views/Auth/LegalViews.swift +++ b/PediFoods/Views/Auth/LegalViews.swift @@ -1,5 +1,8 @@ import SwiftUI import WebKit +#if canImport(LCEssentials) +import LCEssentials +#endif private struct RemotePDFView: View { let url: URL @@ -69,52 +72,32 @@ private struct LegalDocumentScreen: View { @State private var hasError = false var body: some View { - VStack(spacing: 0) { - screenHeader + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text(headerTitle).font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { + ZStack { + RemotePDFView(url: document.url, isLoading: $isLoading, hasError: $hasError) + + if isLoading { + ProgressView() + } + + if hasError { + VStack(spacing: 8) { + Text("Não foi possível carregar \(title.lowercased()).") + .font(AppTypography.body) + .foregroundStyle(AppColors.textPrimary) + .multilineTextAlignment(.center) + } .padding(24) - - ZStack { - RemotePDFView(url: document.url, isLoading: $isLoading, hasError: $hasError) - - if isLoading { - ProgressView() - } - - if hasError { - VStack(spacing: 8) { - Text("Não foi possível carregar \(title.lowercased()).") - .font(AppTypography.body) - .foregroundStyle(AppColors.textPrimary) - .multilineTextAlignment(.center) - } - .padding(24) - } } } .background((colorScheme == .dark ? Color.black : AppColors.backgroundLight).ignoresSafeArea()) - .appHiddenNavigationBar() - .navigationBarBackButtonHidden(true) - } - - private var screenHeader: some View { - ZStack { - Text(headerTitle) - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } } } diff --git a/PediFoods/Views/Main/AddAddressFormView.swift b/PediFoods/Views/Main/AddAddressFormView.swift index 505b510..a7eb9e1 100644 --- a/PediFoods/Views/Main/AddAddressFormView.swift +++ b/PediFoods/Views/Main/AddAddressFormView.swift @@ -1,4 +1,7 @@ import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct AddAddressFormView: View { @Environment(\.dismiss) var dismiss @@ -31,18 +34,19 @@ struct AddAddressFormView: View { } var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text(existingAddress == nil ? "Novo endereço" : "Editar endereço").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ZStack { AppColors.backgroundLight.ignoresSafeArea() ScrollView(showsIndicators: false) { VStack(spacing: 0) { - screenHeader( - title: existingAddress == nil ? "Novo endereço" : "Editar endereço", - onBack: { dismiss() } - ) - .padding(.horizontal, 20) - .padding(.bottom, 20) - VStack(alignment: .leading, spacing: 16) { LoginField(icon: "tag", placeholder: "Ex: Casa, Trabalho", keyboardType: .default, text: $label) LoginField(icon: "mail", placeholder: "CEP", keyboardType: .numberPad, text: $zipCode) @@ -115,27 +119,6 @@ struct AddAddressFormView: View { } } - private func screenHeader(title: String, onBack: @escaping () -> Void) -> some View { - ZStack { - Text(title) - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: onBack) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private func saveAddress() { guard !isLoading else { return } let latLong: [Double]? = { diff --git a/PediFoods/Views/Main/AddCardFormView.swift b/PediFoods/Views/Main/AddCardFormView.swift index f2ba942..54cc53f 100644 --- a/PediFoods/Views/Main/AddCardFormView.swift +++ b/PediFoods/Views/Main/AddCardFormView.swift @@ -1,4 +1,7 @@ import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct AddCardFormView: View { let appState: AppState @@ -53,10 +56,16 @@ struct AddCardFormView: View { } var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Novo Cartão").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(spacing: 20) { - screenHeader - formSection("Dados do Cartão") { cardNumberField labeledField("Nome no cartão", placeholder: "Como impresso no cartão", text: $holderName, autocap: true) @@ -240,26 +249,6 @@ struct AddCardFormView: View { // MARK: - Sub-views - private var screenHeader: some View { - ZStack { - Text("Novo Cartão") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } private var cardNumberField: some View { VStack(alignment: .leading, spacing: 6) { diff --git a/PediFoods/Views/Main/AddressesView.swift b/PediFoods/Views/Main/AddressesView.swift index 2a19559..e61c7a8 100644 --- a/PediFoods/Views/Main/AddressesView.swift +++ b/PediFoods/Views/Main/AddressesView.swift @@ -1,4 +1,7 @@ import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct AddressesView: View { let message: String? @@ -17,14 +20,20 @@ struct AddressesView: View { let tabBarClearance: CGFloat = 96 var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Meus Endereços").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ZStack { AppColors.backgroundLight .ignoresSafeArea() ScrollView(showsIndicators: false) { VStack(spacing: 20) { - screenHeader(title: "Meus Endereços", onBack: { dismiss() }) - if let message { Text(message) .font(AppTypography.body) @@ -173,28 +182,6 @@ struct AddressesView: View { } } - private func screenHeader(title: String, onBack: @escaping () -> Void) -> some View { - ZStack { - Text(title) - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - - HStack { - Button(action: onBack) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private func selectAddress(_ address: CustomerAddress) { appState.address.selectedId = address.id let label = (address.label ?? "").trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/PediFoods/Views/Main/CheckoutView.swift b/PediFoods/Views/Main/CheckoutView.swift index 0bd4df7..645517e 100644 --- a/PediFoods/Views/Main/CheckoutView.swift +++ b/PediFoods/Views/Main/CheckoutView.swift @@ -1,5 +1,8 @@ import SwiftUI import UIKit +#if canImport(LCEssentials) +import LCEssentials +#endif struct CheckoutView: View { @Binding var appState: AppState @@ -106,33 +109,11 @@ struct CheckoutView: View { } var body: some View { - ScrollView(showsIndicators: false) { - VStack(alignment: .leading, spacing: 18) { - screenHeader - deliveryTypeSection - addressSection - orderSummarySection - paymentSection - - if let errorMessage, errorMessage.isEmpty == false { - Text(errorMessage) - .font(.caption) - .foregroundStyle(Color.red) - } - - if let addressValidationMessage, addressValidationMessage.isEmpty == false { - Text(addressValidationMessage) - .font(.caption) - .foregroundStyle(addressValidationBlocked ? Color.red : AppColors.primary) - } - } - .padding(.horizontal, 20) - .padding(.top, 16) - .padding(.bottom, 10) + LCENavigationView { + content } - .background(AppColors.backgroundLight) - .appHiddenNavigationBar() - .navigationBarBackButtonHidden(true) + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Finalizar Pedido").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) .appBottomSafeAreaInset { bottomBar } @@ -232,25 +213,31 @@ struct CheckoutView: View { } } - private var screenHeader: some View { - ZStack { - Text("Finalizar Pedido") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) + private var content: some View { + ScrollView(showsIndicators: false) { + VStack(alignment: .leading, spacing: 18) { + deliveryTypeSection + addressSection + orderSummarySection + paymentSection + + if let errorMessage, errorMessage.isEmpty == false { + Text(errorMessage) + .font(.caption) + .foregroundStyle(Color.red) + } + + if let addressValidationMessage, addressValidationMessage.isEmpty == false { + Text(addressValidationMessage) + .font(.caption) + .foregroundStyle(addressValidationBlocked ? Color.red : AppColors.primary) } - .buttonStyle(.plain) - Spacer() } + .padding(.horizontal, 20) + .padding(.top, 16) + .padding(.bottom, 10) } + .background(AppColors.backgroundLight) } private var deliveryTypeSection: some View { @@ -730,9 +717,59 @@ struct PaymentPixView: View { } var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Pagamento via PIX").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + .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 + if updated.isPaymentConfirmed { + onPaymentConfirmed?() + openTrackingOnce() + } + } + tracker.start(orderId: currentContext.orderId, jwt: DefaultTokenStore().jwt) + } + .task { + while Task.isCancelled == false { + currentTime = Date() + if isPixExpired { + showPixExpiredSnackbar() + return + } + try? await Task.sleep(nanoseconds: 1_000_000_000) + } + } + .onDisappear { + tracker.stop() + } + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(spacing: 14) { - screenHeader RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous) .fill(AppColors.surface) .overlay( @@ -824,72 +861,6 @@ struct PaymentPixView: View { .padding(20) } .background(AppColors.backgroundLight) - .appHiddenNavigationBar() - .navigationBarBackButtonHidden(true) - .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 - if updated.isPaymentConfirmed { - onPaymentConfirmed?() - openTrackingOnce() - } - } - tracker.start(orderId: currentContext.orderId, jwt: DefaultTokenStore().jwt) - } - .task { - while Task.isCancelled == false { - currentTime = Date() - if isPixExpired { - showPixExpiredSnackbar() - return - } - try? await Task.sleep(nanoseconds: 1_000_000_000) - } - } - .onDisappear { - tracker.stop() - } - } - - private var screenHeader: some View { - ZStack { - Text("Pagamento via PIX") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } } private func openTrackingOnce() { diff --git a/PediFoods/Views/Main/OrderDetailsView.swift b/PediFoods/Views/Main/OrderDetailsView.swift index 15e4413..7e0f3fa 100644 --- a/PediFoods/Views/Main/OrderDetailsView.swift +++ b/PediFoods/Views/Main/OrderDetailsView.swift @@ -1,4 +1,7 @@ import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct OrderDetailsView: View { let order: PublicOrderResult @@ -15,9 +18,16 @@ struct OrderDetailsView: View { @State private var showClearCartAlert = false var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Detalhes do Pedido").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(spacing: 14) { - screenHeader statusCard storeCard itemsCard @@ -81,27 +91,6 @@ struct OrderDetailsView: View { } } - private var screenHeader: some View { - ZStack { - Text("Detalhes do Pedido") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private var statusCard: some View { HStack(spacing: 14) { Circle() diff --git a/PediFoods/Views/Main/OrderTrackingView.swift b/PediFoods/Views/Main/OrderTrackingView.swift index 4311f2f..e7dac35 100644 --- a/PediFoods/Views/Main/OrderTrackingView.swift +++ b/PediFoods/Views/Main/OrderTrackingView.swift @@ -1,5 +1,8 @@ import SwiftUI import UIKit +#if canImport(LCEssentials) +import LCEssentials +#endif private struct TrackingStep: Identifiable { let id: String @@ -29,9 +32,25 @@ struct OrderTrackingView: View { @State var showPushOptInAlert = false var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { + if let postOrderBack { + postOrderBack() + } else { + dismiss() + } + } + .setTitle(text: Text("Pedido \(displayOrderTitle)").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + .navigationDestination(item: $reviewDraft) { draft in + MyReviewsView(initialOrder: draft) + } + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(spacing: 16) { - screenHeader topHeader orderTitleSection statusBanner @@ -82,9 +101,6 @@ struct OrderTrackingView: View { tracker.stop() detachReviewSavedObserver() } - .navigationDestination(item: $reviewDraft) { draft in - MyReviewsView(initialOrder: draft) - } } private func attachReviewSavedObserverIfNeeded() { @@ -110,33 +126,6 @@ struct OrderTrackingView: View { self.reviewSavedObserver = nil } - private var screenHeader: some View { - ZStack { - Text("Pedido \(displayOrderTitle)") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { - if let postOrderBack { - postOrderBack() - } else { - dismiss() - } - }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private var topHeader: some View { HStack(spacing: 10) { Circle() diff --git a/PediFoods/Views/Main/OrdersView.swift b/PediFoods/Views/Main/OrdersView.swift index 9f6d8b0..e4951c7 100644 --- a/PediFoods/Views/Main/OrdersView.swift +++ b/PediFoods/Views/Main/OrdersView.swift @@ -1,4 +1,7 @@ import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct OrdersView: View { @Binding var appState: AppState @@ -14,10 +17,16 @@ struct OrdersView: View { @State var selectedOrderRoute: OrderRouteContext? = nil var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Meus Pedidos").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 14) { - screenHeader(title: "Meus Pedidos", onBack: { dismiss() }) - if isLoading { ProgressView() .frame(maxWidth: .infinity) @@ -77,28 +86,6 @@ struct OrdersView: View { } } - private func screenHeader(title: String, onBack: @escaping () -> Void) -> some View { - ZStack { - Text(title) - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - - HStack { - Button(action: onBack) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private func orderCard(_ order: AppOrderSummary) -> some View { let status = orderVisualStatus(for: order) let detailsRoute = OrderRouteContext( diff --git a/PediFoods/Views/Main/PizzaFlavorAddonsSheet.swift b/PediFoods/Views/Main/PizzaFlavorAddonsSheet.swift index 377cfef..8ae2f7a 100644 --- a/PediFoods/Views/Main/PizzaFlavorAddonsSheet.swift +++ b/PediFoods/Views/Main/PizzaFlavorAddonsSheet.swift @@ -1,5 +1,8 @@ import Foundation import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct PizzaFlavorAddonsSheet: View { let flavor: StoreCatalogProduct @@ -8,9 +11,16 @@ struct PizzaFlavorAddonsSheet: View { @Environment(\.dismiss) var dismiss var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Adicionais").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 12) { - screenHeader Text(flavor.name) .font(AppTypography.heading2) .foregroundStyle(AppColors.textPrimary) @@ -82,26 +92,6 @@ struct PizzaFlavorAddonsSheet: View { .navigationBarBackButtonHidden(true) } - private var screenHeader: some View { - ZStack { - Text("Adicionais") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } private func increment(_ addonId: String) { quantities[addonId, default: 0] += 1 diff --git a/PediFoods/Views/Main/PizzaProductDetailSheet.swift b/PediFoods/Views/Main/PizzaProductDetailSheet.swift index d523a2c..a2af392 100644 --- a/PediFoods/Views/Main/PizzaProductDetailSheet.swift +++ b/PediFoods/Views/Main/PizzaProductDetailSheet.swift @@ -1,5 +1,8 @@ import Foundation import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct PizzaProductDetailSheet: View { let category: StoreCatalogCategory @@ -216,9 +219,16 @@ struct PizzaProductDetailSheet: View { } var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Monte sua pizza").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 16) { - screenHeader Rectangle() .fill(AppColors.brandSoft) .frame(maxWidth: .infinity) @@ -340,25 +350,5 @@ struct PizzaProductDetailSheet: View { } } - private var screenHeader: some View { - ZStack { - Text("Monte sua pizza") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } } diff --git a/PediFoods/Views/Main/ProductDetailSheet.swift b/PediFoods/Views/Main/ProductDetailSheet.swift index 08c6e22..ad6a4c3 100644 --- a/PediFoods/Views/Main/ProductDetailSheet.swift +++ b/PediFoods/Views/Main/ProductDetailSheet.swift @@ -1,5 +1,8 @@ import Foundation import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct ProductDetailSheet: View { let product: StoreCatalogProduct @@ -70,9 +73,16 @@ struct ProductDetailSheet: View { } var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Detalhes").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 16) { - screenHeader AsyncStoreImage(imageURL: imageURL) .frame(height: 220) .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusXL, style: .continuous)) @@ -243,26 +253,6 @@ struct ProductDetailSheet: View { } } - private var screenHeader: some View { - ZStack { - Text("Detalhes") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } private func formatCurrency(_ value: Double) -> String { String(format: "R$ %.2f", value).replacingOccurrences(of: ".", with: ",") diff --git a/PediFoods/Views/Main/PublicLocationPickerView.swift b/PediFoods/Views/Main/PublicLocationPickerView.swift index f5263e1..1e8e2d2 100644 --- a/PediFoods/Views/Main/PublicLocationPickerView.swift +++ b/PediFoods/Views/Main/PublicLocationPickerView.swift @@ -1,4 +1,7 @@ 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 @@ -28,12 +31,18 @@ struct PublicLocationPickerView: View { } 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)) + } + + private var content: some View { ZStack { AppColors.backgroundLight.ignoresSafeArea() VStack(spacing: 20) { - header - if isLoading { ProgressView() .padding(.top, 40) @@ -66,29 +75,6 @@ struct PublicLocationPickerView: View { } } - private var header: some View { - ZStack { - Text(step == .state ? "Escolha seu estado" : "Escolha sua cidade") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - - HStack { - Button(action: back) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - .padding(.horizontal, 20) - } - private var list: some View { ScrollView(showsIndicators: false) { LazyVStack(spacing: 12) { diff --git a/PediFoods/Views/Main/ReviewsView.swift b/PediFoods/Views/Main/ReviewsView.swift index cacaaff..f01fa66 100644 --- a/PediFoods/Views/Main/ReviewsView.swift +++ b/PediFoods/Views/Main/ReviewsView.swift @@ -31,9 +31,21 @@ struct MyReviewsView: View { } var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Minhas Avaliações").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + .navigationDestination(item: $selectedDraft) { draft in + OrderReviewView(draft: draft) { + Task { await loadReviewsFromBackend(forceRefresh: true) } + } + } + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(spacing: 12) { - screenHeader if isLoading && reviews.isEmpty && pendingReviews.isEmpty { ProgressView() .frame(maxWidth: .infinity) @@ -69,13 +81,6 @@ struct MyReviewsView: View { .padding(.bottom, UIDevice.bottomNotch + 30) } .background(AppColors.backgroundLight) - .appHiddenNavigationBar() - .navigationBarBackButtonHidden(true) - .navigationDestination(item: $selectedDraft) { draft in - OrderReviewView(draft: draft) { - Task { await loadReviewsFromBackend(forceRefresh: true) } - } - } .onAppear { Task { await loadReviewsFromBackend(forceRefresh: true) } } @@ -91,27 +96,6 @@ struct MyReviewsView: View { } } - private var screenHeader: some View { - ZStack { - Text("Minhas Avaliações") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private var emptyState: some View { VStack(spacing: 8) { Text("Você não tem avaliações nem pendências no momento.") @@ -458,9 +442,16 @@ struct OrderReviewView: View { @State var orderItems: [PublicOrderItem] = [] var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Avaliar Pedido").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 24) { - screenHeader topSection orderItemsSection if existingReview != nil { @@ -515,27 +506,6 @@ struct OrderReviewView: View { } } - private var screenHeader: some View { - ZStack { - Text("Avaliar Pedido") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private var newReviewContent: some View { VStack(alignment: .leading, spacing: 24) { starsSection diff --git a/PediFoods/Views/Main/SavedCardsView.swift b/PediFoods/Views/Main/SavedCardsView.swift index f5c6c64..097cca7 100644 --- a/PediFoods/Views/Main/SavedCardsView.swift +++ b/PediFoods/Views/Main/SavedCardsView.swift @@ -1,4 +1,7 @@ import SwiftUI +#if canImport(LCEssentials) +import LCEssentials +#endif struct SavedCardsView: View { @Binding var appState: AppState @@ -15,13 +18,19 @@ struct SavedCardsView: View { private let tabBarClearance: CGFloat = 96 var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Meus Cartões").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ZStack { AppColors.backgroundLight.ignoresSafeArea() ScrollView(showsIndicators: false) { VStack(spacing: 20) { - screenHeader - VStack(spacing: 12) { if isLoading { ProgressView() @@ -86,27 +95,6 @@ struct SavedCardsView: View { .task { await loadCards() } } - private var screenHeader: some View { - ZStack { - Text("Meus Cartões") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private var addCardButton: some View { ZStack(alignment: .bottom) { Rectangle() diff --git a/PediFoods/Views/Main/UserProfileView.swift b/PediFoods/Views/Main/UserProfileView.swift index ad6560c..6f11b15 100644 --- a/PediFoods/Views/Main/UserProfileView.swift +++ b/PediFoods/Views/Main/UserProfileView.swift @@ -3,6 +3,9 @@ import SwiftUI import PhotosUI import UIKit #endif +#if canImport(LCEssentials) +import LCEssentials +#endif struct UserProfileView: View { @Binding var appState: AppState @@ -26,9 +29,16 @@ struct UserProfileView: View { #endif var body: some View { + LCENavigationView { + content + } + .setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() } + .setTitle(text: Text("Meu Perfil").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary)) + } + + private var content: some View { ScrollView(showsIndicators: false) { VStack(spacing: 22) { - screenHeader avatarSection formSection preferencesSection @@ -57,27 +67,6 @@ struct UserProfileView: View { #endif } - private var screenHeader: some View { - ZStack { - Text("Meu Perfil") - .font(AppTypography.heading2) - .foregroundStyle(AppColors.textPrimary) - HStack { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 24, weight: .semibold)) - .foregroundStyle(AppColors.textPrimary) - .frame(width: 52, height: 52) - .background(AppColors.surface) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.06), radius: 8, y: 2) - } - .buttonStyle(.plain) - Spacer() - } - } - } - private var avatarSection: some View { VStack(spacing: 12) { Circle()