[lcenavigationview-adoption] Adopt LCENavigationView across 15 screens
Standardizes the back button to a single shared component (AppBackButtonIcon, 32x32/14pt) instead of ~16 hand-duplicated 52x52/24pt copies, and wires each screen through LCENavigationView per this project's mandatory-usage rule. Home and Store Detail intentionally excluded - both have bespoke header designs (collapsing header, hero-image overlay) this change would visually disrupt.
This commit is contained in:
@@ -114,6 +114,7 @@
|
|||||||
CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC381D02A37312BE360024F /* Buttons.swift */; };
|
CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC381D02A37312BE360024F /* Buttons.swift */; };
|
||||||
CD96980F3D7B603126FDE6AC /* URLProtocolStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51798A414469C911D44F0F6 /* URLProtocolStub.swift */; };
|
CD96980F3D7B603126FDE6AC /* URLProtocolStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51798A414469C911D44F0F6 /* URLProtocolStub.swift */; };
|
||||||
CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.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 */; };
|
D2FB3C624B3C27E59BCA997A /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA34EE194C71755B3C82F30 /* MainTabView.swift */; };
|
||||||
D3EC6E0A68B809F7A3370B1B /* ProductDetailSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553A05B9EAD0B9D0D9F1B369 /* ProductDetailSheet.swift */; };
|
D3EC6E0A68B809F7A3370B1B /* ProductDetailSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553A05B9EAD0B9D0D9F1B369 /* ProductDetailSheet.swift */; };
|
||||||
D48148B155BA2F76C41C0787 /* HomeFiltersStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FBD7D809ACE1840165A9DCC /* HomeFiltersStateTests.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 = "<group>"; };
|
EB95D6804877D2ED244811D0 /* Inputs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Inputs.swift; sourceTree = "<group>"; };
|
||||||
ECBFCE20C54A79DEA8F4512B /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; };
|
ECBFCE20C54A79DEA8F4512B /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; };
|
||||||
EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedRemoteImage.swift; sourceTree = "<group>"; };
|
EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedRemoteImage.swift; sourceTree = "<group>"; };
|
||||||
|
52EEF6262A813BB8274266C8 /* AppBackButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppBackButtonIcon.swift; sourceTree = "<group>"; };
|
||||||
F0B079DFAB606DCA87A536C9 /* LaunchSplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchSplashView.swift; sourceTree = "<group>"; };
|
F0B079DFAB606DCA87A536C9 /* LaunchSplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchSplashView.swift; sourceTree = "<group>"; };
|
||||||
F18F768F7A2A47111A85FE88 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
|
F18F768F7A2A47111A85FE88 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
|
||||||
F2B2C5BBDFF4473C472F45B4 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
F2B2C5BBDFF4473C472F45B4 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
||||||
@@ -558,6 +560,7 @@
|
|||||||
children = (
|
children = (
|
||||||
6EC381D02A37312BE360024F /* Buttons.swift */,
|
6EC381D02A37312BE360024F /* Buttons.swift */,
|
||||||
EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */,
|
EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */,
|
||||||
|
52EEF6262A813BB8274266C8 /* AppBackButtonIcon.swift */,
|
||||||
EB95D6804877D2ED244811D0 /* Inputs.swift */,
|
EB95D6804877D2ED244811D0 /* Inputs.swift */,
|
||||||
C4745C12F14B695C77DFE178 /* SnackbarOverlay.swift */,
|
C4745C12F14B695C77DFE178 /* SnackbarOverlay.swift */,
|
||||||
31916C1A4005396ADA5FF3BC /* StoreCard.swift */,
|
31916C1A4005396ADA5FF3BC /* StoreCard.swift */,
|
||||||
@@ -779,6 +782,7 @@
|
|||||||
3689239282AADF06783CBBEA /* AuthFormatters.swift in Sources */,
|
3689239282AADF06783CBBEA /* AuthFormatters.swift in Sources */,
|
||||||
CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */,
|
CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */,
|
||||||
CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */,
|
CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */,
|
||||||
|
38452FD60CBDE248531DA484 /* AppBackButtonIcon.swift in Sources */,
|
||||||
DE499D6150C15EB046AF7963 /* CartView.swift in Sources */,
|
DE499D6150C15EB046AF7963 /* CartView.swift in Sources */,
|
||||||
70EA850C067AB6C6B87539E2 /* CheckoutTypes.swift in Sources */,
|
70EA850C067AB6C6B87539E2 /* CheckoutTypes.swift in Sources */,
|
||||||
41BE548D929D3814A736FA12 /* CheckoutView+Logic.swift in Sources */,
|
41BE548D929D3814A736FA12 /* CheckoutView+Logic.swift in Sources */,
|
||||||
|
|||||||
13
PediFoods/Components/AppBackButtonIcon.swift
Normal file
13
PediFoods/Components/AppBackButtonIcon.swift
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import WebKit
|
import WebKit
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
private struct RemotePDFView: View {
|
private struct RemotePDFView: View {
|
||||||
let url: URL
|
let url: URL
|
||||||
@@ -69,10 +72,14 @@ private struct LegalDocumentScreen: View {
|
|||||||
@State private var hasError = false
|
@State private var hasError = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
LCENavigationView {
|
||||||
screenHeader
|
content
|
||||||
.padding(24)
|
}
|
||||||
|
.setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() }
|
||||||
|
.setTitle(text: Text(headerTitle).font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var content: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
RemotePDFView(url: document.url, isLoading: $isLoading, hasError: $hasError)
|
RemotePDFView(url: document.url, isLoading: $isLoading, hasError: $hasError)
|
||||||
|
|
||||||
@@ -90,31 +97,7 @@ private struct LegalDocumentScreen: View {
|
|||||||
.padding(24)
|
.padding(24)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.background((colorScheme == .dark ? Color.black : AppColors.backgroundLight).ignoresSafeArea())
|
.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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct AddAddressFormView: View {
|
struct AddAddressFormView: View {
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
@@ -31,18 +34,19 @@ struct AddAddressFormView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some 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 {
|
ZStack {
|
||||||
AppColors.backgroundLight.ignoresSafeArea()
|
AppColors.backgroundLight.ignoresSafeArea()
|
||||||
|
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 0) {
|
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) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
LoginField(icon: "tag", placeholder: "Ex: Casa, Trabalho", keyboardType: .default, text: $label)
|
LoginField(icon: "tag", placeholder: "Ex: Casa, Trabalho", keyboardType: .default, text: $label)
|
||||||
LoginField(icon: "mail", placeholder: "CEP", keyboardType: .numberPad, text: $zipCode)
|
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() {
|
private func saveAddress() {
|
||||||
guard !isLoading else { return }
|
guard !isLoading else { return }
|
||||||
let latLong: [Double]? = {
|
let latLong: [Double]? = {
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct AddCardFormView: View {
|
struct AddCardFormView: View {
|
||||||
let appState: AppState
|
let appState: AppState
|
||||||
@@ -53,10 +56,16 @@ struct AddCardFormView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some 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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
screenHeader
|
|
||||||
|
|
||||||
formSection("Dados do Cartão") {
|
formSection("Dados do Cartão") {
|
||||||
cardNumberField
|
cardNumberField
|
||||||
labeledField("Nome no cartão", placeholder: "Como impresso no cartão", text: $holderName, autocap: true)
|
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
|
// 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 {
|
private var cardNumberField: some View {
|
||||||
VStack(alignment: .leading, spacing: 6) {
|
VStack(alignment: .leading, spacing: 6) {
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct AddressesView: View {
|
struct AddressesView: View {
|
||||||
let message: String?
|
let message: String?
|
||||||
@@ -17,14 +20,20 @@ struct AddressesView: View {
|
|||||||
let tabBarClearance: CGFloat = 96
|
let tabBarClearance: CGFloat = 96
|
||||||
|
|
||||||
var body: some View {
|
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 {
|
ZStack {
|
||||||
AppColors.backgroundLight
|
AppColors.backgroundLight
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
|
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
screenHeader(title: "Meus Endereços", onBack: { dismiss() })
|
|
||||||
|
|
||||||
if let message {
|
if let message {
|
||||||
Text(message)
|
Text(message)
|
||||||
.font(AppTypography.body)
|
.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) {
|
private func selectAddress(_ address: CustomerAddress) {
|
||||||
appState.address.selectedId = address.id
|
appState.address.selectedId = address.id
|
||||||
let label = (address.label ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
|
let label = (address.label ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
import UIKit
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct CheckoutView: View {
|
struct CheckoutView: View {
|
||||||
@Binding var appState: AppState
|
@Binding var appState: AppState
|
||||||
@@ -106,33 +109,11 @@ struct CheckoutView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(showsIndicators: false) {
|
LCENavigationView {
|
||||||
VStack(alignment: .leading, spacing: 18) {
|
content
|
||||||
screenHeader
|
|
||||||
deliveryTypeSection
|
|
||||||
addressSection
|
|
||||||
orderSummarySection
|
|
||||||
paymentSection
|
|
||||||
|
|
||||||
if let errorMessage, errorMessage.isEmpty == false {
|
|
||||||
Text(errorMessage)
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(Color.red)
|
|
||||||
}
|
}
|
||||||
|
.setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() }
|
||||||
if let addressValidationMessage, addressValidationMessage.isEmpty == false {
|
.setTitle(text: Text("Finalizar Pedido").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary))
|
||||||
Text(addressValidationMessage)
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(addressValidationBlocked ? Color.red : AppColors.primary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.top, 16)
|
|
||||||
.padding(.bottom, 10)
|
|
||||||
}
|
|
||||||
.background(AppColors.backgroundLight)
|
|
||||||
.appHiddenNavigationBar()
|
|
||||||
.navigationBarBackButtonHidden(true)
|
|
||||||
.appBottomSafeAreaInset {
|
.appBottomSafeAreaInset {
|
||||||
bottomBar
|
bottomBar
|
||||||
}
|
}
|
||||||
@@ -232,25 +213,31 @@ struct CheckoutView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var screenHeader: some View {
|
private var content: some View {
|
||||||
ZStack {
|
ScrollView(showsIndicators: false) {
|
||||||
Text("Finalizar Pedido")
|
VStack(alignment: .leading, spacing: 18) {
|
||||||
.font(AppTypography.heading2)
|
deliveryTypeSection
|
||||||
.foregroundStyle(AppColors.textPrimary)
|
addressSection
|
||||||
HStack {
|
orderSummarySection
|
||||||
Button(action: { dismiss() }) {
|
paymentSection
|
||||||
Image(systemName: "chevron.left")
|
|
||||||
.font(.system(size: 24, weight: .semibold))
|
if let errorMessage, errorMessage.isEmpty == false {
|
||||||
.foregroundStyle(AppColors.textPrimary)
|
Text(errorMessage)
|
||||||
.frame(width: 52, height: 52)
|
.font(.caption)
|
||||||
.background(AppColors.surface)
|
.foregroundStyle(Color.red)
|
||||||
.clipShape(Circle())
|
|
||||||
.shadow(color: .black.opacity(0.06), radius: 8, y: 2)
|
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
|
||||||
Spacer()
|
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)
|
||||||
|
}
|
||||||
|
.background(AppColors.backgroundLight)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var deliveryTypeSection: some View {
|
private var deliveryTypeSection: some View {
|
||||||
@@ -730,9 +717,59 @@ struct PaymentPixView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some 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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 14) {
|
VStack(spacing: 14) {
|
||||||
screenHeader
|
|
||||||
RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)
|
RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)
|
||||||
.fill(AppColors.surface)
|
.fill(AppColors.surface)
|
||||||
.overlay(
|
.overlay(
|
||||||
@@ -824,72 +861,6 @@ struct PaymentPixView: View {
|
|||||||
.padding(20)
|
.padding(20)
|
||||||
}
|
}
|
||||||
.background(AppColors.backgroundLight)
|
.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() {
|
private func openTrackingOnce() {
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct OrderDetailsView: View {
|
struct OrderDetailsView: View {
|
||||||
let order: PublicOrderResult
|
let order: PublicOrderResult
|
||||||
@@ -15,9 +18,16 @@ struct OrderDetailsView: View {
|
|||||||
@State private var showClearCartAlert = false
|
@State private var showClearCartAlert = false
|
||||||
|
|
||||||
var body: some View {
|
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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 14) {
|
VStack(spacing: 14) {
|
||||||
screenHeader
|
|
||||||
statusCard
|
statusCard
|
||||||
storeCard
|
storeCard
|
||||||
itemsCard
|
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 {
|
private var statusCard: some View {
|
||||||
HStack(spacing: 14) {
|
HStack(spacing: 14) {
|
||||||
Circle()
|
Circle()
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
import UIKit
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
private struct TrackingStep: Identifiable {
|
private struct TrackingStep: Identifiable {
|
||||||
let id: String
|
let id: String
|
||||||
@@ -29,9 +32,25 @@ struct OrderTrackingView: View {
|
|||||||
@State var showPushOptInAlert = false
|
@State var showPushOptInAlert = false
|
||||||
|
|
||||||
var body: some View {
|
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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
screenHeader
|
|
||||||
topHeader
|
topHeader
|
||||||
orderTitleSection
|
orderTitleSection
|
||||||
statusBanner
|
statusBanner
|
||||||
@@ -82,9 +101,6 @@ struct OrderTrackingView: View {
|
|||||||
tracker.stop()
|
tracker.stop()
|
||||||
detachReviewSavedObserver()
|
detachReviewSavedObserver()
|
||||||
}
|
}
|
||||||
.navigationDestination(item: $reviewDraft) { draft in
|
|
||||||
MyReviewsView(initialOrder: draft)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func attachReviewSavedObserverIfNeeded() {
|
private func attachReviewSavedObserverIfNeeded() {
|
||||||
@@ -110,33 +126,6 @@ struct OrderTrackingView: View {
|
|||||||
self.reviewSavedObserver = nil
|
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 {
|
private var topHeader: some View {
|
||||||
HStack(spacing: 10) {
|
HStack(spacing: 10) {
|
||||||
Circle()
|
Circle()
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct OrdersView: View {
|
struct OrdersView: View {
|
||||||
@Binding var appState: AppState
|
@Binding var appState: AppState
|
||||||
@@ -14,10 +17,16 @@ struct OrdersView: View {
|
|||||||
@State var selectedOrderRoute: OrderRouteContext? = nil
|
@State var selectedOrderRoute: OrderRouteContext? = nil
|
||||||
|
|
||||||
var body: some View {
|
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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(alignment: .leading, spacing: 14) {
|
VStack(alignment: .leading, spacing: 14) {
|
||||||
screenHeader(title: "Meus Pedidos", onBack: { dismiss() })
|
|
||||||
|
|
||||||
if isLoading {
|
if isLoading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
.frame(maxWidth: .infinity)
|
.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 {
|
private func orderCard(_ order: AppOrderSummary) -> some View {
|
||||||
let status = orderVisualStatus(for: order)
|
let status = orderVisualStatus(for: order)
|
||||||
let detailsRoute = OrderRouteContext(
|
let detailsRoute = OrderRouteContext(
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct PizzaFlavorAddonsSheet: View {
|
struct PizzaFlavorAddonsSheet: View {
|
||||||
let flavor: StoreCatalogProduct
|
let flavor: StoreCatalogProduct
|
||||||
@@ -8,9 +11,16 @@ struct PizzaFlavorAddonsSheet: View {
|
|||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
var body: some View {
|
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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
screenHeader
|
|
||||||
Text(flavor.name)
|
Text(flavor.name)
|
||||||
.font(AppTypography.heading2)
|
.font(AppTypography.heading2)
|
||||||
.foregroundStyle(AppColors.textPrimary)
|
.foregroundStyle(AppColors.textPrimary)
|
||||||
@@ -82,26 +92,6 @@ struct PizzaFlavorAddonsSheet: View {
|
|||||||
.navigationBarBackButtonHidden(true)
|
.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) {
|
private func increment(_ addonId: String) {
|
||||||
quantities[addonId, default: 0] += 1
|
quantities[addonId, default: 0] += 1
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct PizzaProductDetailSheet: View {
|
struct PizzaProductDetailSheet: View {
|
||||||
let category: StoreCatalogCategory
|
let category: StoreCatalogCategory
|
||||||
@@ -216,9 +219,16 @@ struct PizzaProductDetailSheet: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some 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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
screenHeader
|
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(AppColors.brandSoft)
|
.fill(AppColors.brandSoft)
|
||||||
.frame(maxWidth: .infinity)
|
.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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct ProductDetailSheet: View {
|
struct ProductDetailSheet: View {
|
||||||
let product: StoreCatalogProduct
|
let product: StoreCatalogProduct
|
||||||
@@ -70,9 +73,16 @@ struct ProductDetailSheet: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some 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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
screenHeader
|
|
||||||
AsyncStoreImage(imageURL: imageURL)
|
AsyncStoreImage(imageURL: imageURL)
|
||||||
.frame(height: 220)
|
.frame(height: 220)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusXL, style: .continuous))
|
.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 {
|
private func formatCurrency(_ value: Double) -> String {
|
||||||
String(format: "R$ %.2f", value).replacingOccurrences(of: ".", with: ",")
|
String(format: "R$ %.2f", value).replacingOccurrences(of: ".", with: ",")
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
/// State -> city picker for anonymous browsing (public store locator).
|
/// State -> city picker for anonymous browsing (public store locator).
|
||||||
/// Replaces AddressesView in the address-picker modal when the user is
|
/// Replaces AddressesView in the address-picker modal when the user is
|
||||||
@@ -28,12 +31,18 @@ struct PublicLocationPickerView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some 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 {
|
ZStack {
|
||||||
AppColors.backgroundLight.ignoresSafeArea()
|
AppColors.backgroundLight.ignoresSafeArea()
|
||||||
|
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
header
|
|
||||||
|
|
||||||
if isLoading {
|
if isLoading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
.padding(.top, 40)
|
.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 {
|
private var list: some View {
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
LazyVStack(spacing: 12) {
|
LazyVStack(spacing: 12) {
|
||||||
|
|||||||
@@ -31,9 +31,21 @@ struct MyReviewsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some 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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
screenHeader
|
|
||||||
if isLoading && reviews.isEmpty && pendingReviews.isEmpty {
|
if isLoading && reviews.isEmpty && pendingReviews.isEmpty {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@@ -69,13 +81,6 @@ struct MyReviewsView: View {
|
|||||||
.padding(.bottom, UIDevice.bottomNotch + 30)
|
.padding(.bottom, UIDevice.bottomNotch + 30)
|
||||||
}
|
}
|
||||||
.background(AppColors.backgroundLight)
|
.background(AppColors.backgroundLight)
|
||||||
.appHiddenNavigationBar()
|
|
||||||
.navigationBarBackButtonHidden(true)
|
|
||||||
.navigationDestination(item: $selectedDraft) { draft in
|
|
||||||
OrderReviewView(draft: draft) {
|
|
||||||
Task { await loadReviewsFromBackend(forceRefresh: true) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Task { await loadReviewsFromBackend(forceRefresh: true) }
|
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 {
|
private var emptyState: some View {
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
Text("Você não tem avaliações nem pendências no momento.")
|
Text("Você não tem avaliações nem pendências no momento.")
|
||||||
@@ -458,9 +442,16 @@ struct OrderReviewView: View {
|
|||||||
@State var orderItems: [PublicOrderItem] = []
|
@State var orderItems: [PublicOrderItem] = []
|
||||||
|
|
||||||
var body: some View {
|
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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(alignment: .leading, spacing: 24) {
|
VStack(alignment: .leading, spacing: 24) {
|
||||||
screenHeader
|
|
||||||
topSection
|
topSection
|
||||||
orderItemsSection
|
orderItemsSection
|
||||||
if existingReview != nil {
|
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 {
|
private var newReviewContent: some View {
|
||||||
VStack(alignment: .leading, spacing: 24) {
|
VStack(alignment: .leading, spacing: 24) {
|
||||||
starsSection
|
starsSection
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct SavedCardsView: View {
|
struct SavedCardsView: View {
|
||||||
@Binding var appState: AppState
|
@Binding var appState: AppState
|
||||||
@@ -15,13 +18,19 @@ struct SavedCardsView: View {
|
|||||||
private let tabBarClearance: CGFloat = 96
|
private let tabBarClearance: CGFloat = 96
|
||||||
|
|
||||||
var body: some View {
|
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 {
|
ZStack {
|
||||||
AppColors.backgroundLight.ignoresSafeArea()
|
AppColors.backgroundLight.ignoresSafeArea()
|
||||||
|
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
screenHeader
|
|
||||||
|
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
if isLoading {
|
if isLoading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
@@ -86,27 +95,6 @@ struct SavedCardsView: View {
|
|||||||
.task { await loadCards() }
|
.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 {
|
private var addCardButton: some View {
|
||||||
ZStack(alignment: .bottom) {
|
ZStack(alignment: .bottom) {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import SwiftUI
|
|||||||
import PhotosUI
|
import PhotosUI
|
||||||
import UIKit
|
import UIKit
|
||||||
#endif
|
#endif
|
||||||
|
#if canImport(LCEssentials)
|
||||||
|
import LCEssentials
|
||||||
|
#endif
|
||||||
|
|
||||||
struct UserProfileView: View {
|
struct UserProfileView: View {
|
||||||
@Binding var appState: AppState
|
@Binding var appState: AppState
|
||||||
@@ -26,9 +29,16 @@ struct UserProfileView: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
var body: some View {
|
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) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 22) {
|
VStack(spacing: 22) {
|
||||||
screenHeader
|
|
||||||
avatarSection
|
avatarSection
|
||||||
formSection
|
formSection
|
||||||
preferencesSection
|
preferencesSection
|
||||||
@@ -57,27 +67,6 @@ struct UserProfileView: View {
|
|||||||
#endif
|
#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 {
|
private var avatarSection: some View {
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
Circle()
|
Circle()
|
||||||
|
|||||||
Reference in New Issue
Block a user