feat(checkout): replace dual payment group cards with single toggle switch

Toggle ON = Pagar Pelo App (default), shows in-app methods (PIX, Credit Card)
Toggle OFF = Pagar Na Maquininha, shows store machine methods
Automatically resets paymentMethod on toggle switch
Removes paymentGroupCard helper, saves vertical space
This commit is contained in:
Daniel Arantes Loverde
2026-06-03 15:49:26 -03:00
parent c89c80e8d1
commit 2d27240cee

View File

@@ -301,51 +301,32 @@ struct CheckoutView: View {
.font(AppTypography.overline) .font(AppTypography.overline)
.foregroundStyle(sectionTitleColor) .foregroundStyle(sectionTitleColor)
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 0) {
paymentGroupCard( // Toggle row
title: "Pagar Pelo App", HStack(spacing: 12) {
subtitle: "Mais rápido e seguro", VStack(alignment: .leading, spacing: 2) {
isSelected: useInAppPayment Text(useInAppPayment ? "Pagar Pelo App" : "Pagar Na Maquininha")
) { .font(AppTypography.heading3)
VStack(spacing: 0) { .foregroundStyle(AppColors.textPrimary)
ForEach(availableInAppPaymentMethods, id: \.rawValue) { method in Text(useInAppPayment ? "Mais rápido e seguro" : "Na entrega/retirada com a loja")
paymentRow(method, isInAppGroup: true) .font(.caption)
if method != availableInAppPaymentMethods.last { .foregroundStyle(AppColors.textMuted)
Divider()
} }
} Spacer()
} Toggle("", isOn: Binding(
} onTap: { get: { useInAppPayment },
useInAppPayment = true set: { newValue in
withAnimation(.easeInOut(duration: 0.2)) {
useInAppPayment = newValue
if newValue {
if availableInAppPaymentMethods.contains(paymentMethod) == false { if availableInAppPaymentMethods.contains(paymentMethod) == false {
paymentMethod = .pix paymentMethod = .pix
} }
}
paymentGroupCard(
title: "Pagar Na Maquininha Da Loja",
subtitle: "Pague na entrega/retirada com os métodos aceitos pela loja",
isSelected: useInAppPayment == false
) {
if availableStoreMachineMethods.isEmpty {
Text("Loja não informou métodos presenciais.")
.font(AppTypography.body)
.foregroundStyle(AppColors.textMuted)
.padding(.horizontal, 14)
.padding(.bottom, 14)
} else { } else {
VStack(spacing: 0) { guard availableStoreMachineMethods.isEmpty == false else {
ForEach(availableStoreMachineMethods, id: \.rawValue) { method in useInAppPayment = true
paymentRow(method, isInAppGroup: false) return
if method != availableStoreMachineMethods.last {
Divider()
} }
}
}
}
} onTap: {
guard availableStoreMachineMethods.isEmpty == false else { return }
useInAppPayment = false
if availableStoreMachineMethods.contains(paymentMethod) == false, if availableStoreMachineMethods.contains(paymentMethod) == false,
let first = availableStoreMachineMethods.first { let first = availableStoreMachineMethods.first {
paymentMethod = first paymentMethod = first
@@ -353,6 +334,42 @@ struct CheckoutView: View {
} }
} }
} }
))
.labelsHidden()
.tint(AppColors.primary)
}
.padding(14)
Divider()
.padding(.horizontal, 14)
// Methods list
if useInAppPayment {
ForEach(availableInAppPaymentMethods, id: \.rawValue) { method in
paymentRow(method, isInAppGroup: true)
if method != availableInAppPaymentMethods.last {
Divider().padding(.horizontal, 14)
}
}
} else {
if availableStoreMachineMethods.isEmpty {
Text("Loja não informou métodos presenciais.")
.font(AppTypography.body)
.foregroundStyle(AppColors.textMuted)
.padding(14)
} else {
ForEach(availableStoreMachineMethods, id: \.rawValue) { method in
paymentRow(method, isInAppGroup: false)
if method != availableStoreMachineMethods.last {
Divider().padding(.horizontal, 14)
}
}
}
}
}
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous))
}
} }
private func paymentRow(_ method: CheckoutPaymentMethod, isInAppGroup: Bool) -> some View { private func paymentRow(_ method: CheckoutPaymentMethod, isInAppGroup: Bool) -> some View {
@@ -405,44 +422,6 @@ struct CheckoutView: View {
return method.subtitle return method.subtitle
} }
private func paymentGroupCard<Content: View>(
title: String,
subtitle: String,
isSelected: Bool,
@ViewBuilder content: () -> Content,
onTap: @escaping () -> Void
) -> some View {
VStack(alignment: .leading, spacing: 10) {
HStack(spacing: 10) {
VStack(alignment: .leading, spacing: 2) {
Text(title)
.font(AppTypography.heading3)
.foregroundStyle(AppColors.textPrimary)
Text(subtitle)
.font(.caption)
.foregroundStyle(AppColors.textMuted)
}
Spacer()
Circle()
.stroke(isSelected ? AppColors.tertiary : AppColors.textMuted.opacity(0.45), lineWidth: 2)
.frame(width: 22, height: 22)
.background(
Circle()
.fill(isSelected ? AppColors.tertiary : Color.clear)
)
}
.padding(.horizontal, 14)
.padding(.top, 14)
.appContentShape(Rectangle())
.onTapGesture(perform: onTap)
content()
.allowsHitTesting(isSelected)
.opacity(isSelected ? 1 : 0.82)
}
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous))
}
private var bottomBar: some View { private var bottomBar: some View {
VStack(spacing: 12) { VStack(spacing: 12) {