|
|
@@ -1,7 +1,15 @@
|
|
|
import SwiftUI
|
|
|
|
|
|
struct PremiumFeaturesView: View {
|
|
|
+ enum PlanOption: String {
|
|
|
+ case perpetual
|
|
|
+ case yearly
|
|
|
+ }
|
|
|
+
|
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
+ @State private var selectedPlan: PlanOption = .perpetual
|
|
|
+ @State private var showingCheckout = false
|
|
|
+ @State private var showingSuccessAlert = false
|
|
|
|
|
|
private let featureRows: [(String, String)] = [
|
|
|
("Desktop Widgets", "Add frequently used apps as desktop widgets"),
|
|
|
@@ -82,17 +90,25 @@ struct PremiumFeaturesView: View {
|
|
|
title: "Perpetual",
|
|
|
price: "Rs 9,900.00",
|
|
|
subtitle: "Own the software for lifetime",
|
|
|
- isPrimary: true
|
|
|
+ badgeText: "One-time payment",
|
|
|
+ isSelected: selectedPlan == .perpetual
|
|
|
)
|
|
|
+ .onTapGesture {
|
|
|
+ selectedPlan = .perpetual
|
|
|
+ }
|
|
|
|
|
|
priceCard(
|
|
|
title: "Yearly",
|
|
|
price: "Rs 7,900.00/year",
|
|
|
subtitle: "Rs 658.33/mo",
|
|
|
- isPrimary: false
|
|
|
+ badgeText: nil,
|
|
|
+ isSelected: selectedPlan == .yearly
|
|
|
)
|
|
|
+ .onTapGesture {
|
|
|
+ selectedPlan = .yearly
|
|
|
+ }
|
|
|
|
|
|
- Button(action: {}) {
|
|
|
+ Button(action: { showingCheckout = true }) {
|
|
|
Text("Continue")
|
|
|
.font(.system(size: 16, weight: .bold))
|
|
|
.foregroundStyle(.white)
|
|
|
@@ -122,6 +138,22 @@ struct PremiumFeaturesView: View {
|
|
|
.padding(16)
|
|
|
}
|
|
|
}
|
|
|
+ .sheet(isPresented: $showingCheckout) {
|
|
|
+ PaymentCheckoutView(
|
|
|
+ selectedPlan: selectedPlan,
|
|
|
+ onCancel: { showingCheckout = false },
|
|
|
+ onConfirm: {
|
|
|
+ showingCheckout = false
|
|
|
+ showingSuccessAlert = true
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .frame(minWidth: 500, minHeight: 520)
|
|
|
+ }
|
|
|
+ .alert("Payment Successful", isPresented: $showingSuccessAlert) {
|
|
|
+ Button("Done") { dismiss() }
|
|
|
+ } message: {
|
|
|
+ Text("Your \(selectedPlan == .perpetual ? "Perpetual" : "Yearly") plan has been activated.")
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private func featureCard(title: String, subtitle: String, color: Color) -> some View {
|
|
|
@@ -155,7 +187,7 @@ struct PremiumFeaturesView: View {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
- private func priceCard(title: String, price: String, subtitle: String, isPrimary: Bool) -> some View {
|
|
|
+ private func priceCard(title: String, price: String, subtitle: String, badgeText: String?, isSelected: Bool) -> some View {
|
|
|
HStack(alignment: .center) {
|
|
|
VStack(alignment: .leading, spacing: 5) {
|
|
|
Text(title)
|
|
|
@@ -174,8 +206,8 @@ struct PremiumFeaturesView: View {
|
|
|
.foregroundStyle(.white)
|
|
|
.minimumScaleFactor(0.7)
|
|
|
.lineLimit(1)
|
|
|
- if isPrimary {
|
|
|
- Text("One-time payment")
|
|
|
+ if let badgeText {
|
|
|
+ Text(badgeText)
|
|
|
.font(.system(size: 7.5, weight: .bold))
|
|
|
.foregroundStyle(.white)
|
|
|
.padding(.horizontal, 8)
|
|
|
@@ -190,12 +222,134 @@ struct PremiumFeaturesView: View {
|
|
|
.padding(10)
|
|
|
.background(
|
|
|
RoundedRectangle(cornerRadius: 14, style: .continuous)
|
|
|
- .stroke(isPrimary ? Color.blue.opacity(0.95) : Color.white.opacity(0.4), lineWidth: 1.5)
|
|
|
+ .stroke(isSelected ? Color.blue.opacity(0.95) : Color.white.opacity(0.4), lineWidth: isSelected ? 2 : 1.5)
|
|
|
.background(
|
|
|
RoundedRectangle(cornerRadius: 14, style: .continuous)
|
|
|
- .fill(Color.white.opacity(0.05))
|
|
|
+ .fill(isSelected ? Color.blue.opacity(0.12) : Color.white.opacity(0.05))
|
|
|
)
|
|
|
)
|
|
|
+ .contentShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private struct PaymentCheckoutView: View {
|
|
|
+ let selectedPlan: PremiumFeaturesView.PlanOption
|
|
|
+ let onCancel: () -> Void
|
|
|
+ let onConfirm: () -> Void
|
|
|
+
|
|
|
+ @State private var cardholderName = ""
|
|
|
+ @State private var cardNumber = ""
|
|
|
+ @State private var expiry = ""
|
|
|
+ @State private var cvv = ""
|
|
|
+ @State private var saveCard = true
|
|
|
+
|
|
|
+ private var amountText: String {
|
|
|
+ switch selectedPlan {
|
|
|
+ case .perpetual:
|
|
|
+ return "Rs 9,900.00"
|
|
|
+ case .yearly:
|
|
|
+ return "Rs 7,900.00/year"
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private var canSubmit: Bool {
|
|
|
+ !cardholderName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
|
|
|
+ cardNumber.filter(\.isNumber).count >= 12 &&
|
|
|
+ expiry.count >= 4 &&
|
|
|
+ cvv.filter(\.isNumber).count >= 3
|
|
|
+ }
|
|
|
+
|
|
|
+ var body: some View {
|
|
|
+ ZStack {
|
|
|
+ LinearGradient(
|
|
|
+ colors: [Color.black.opacity(0.95), Color(red: 0.09, green: 0.11, blue: 0.16)],
|
|
|
+ startPoint: .topLeading,
|
|
|
+ endPoint: .bottomTrailing
|
|
|
+ )
|
|
|
+ .ignoresSafeArea()
|
|
|
+
|
|
|
+ VStack(alignment: .leading, spacing: 16) {
|
|
|
+ HStack {
|
|
|
+ Text("Add Credit Card")
|
|
|
+ .font(.system(size: 22, weight: .bold))
|
|
|
+ .foregroundStyle(.white)
|
|
|
+ Spacer()
|
|
|
+ Button("Cancel", action: onCancel)
|
|
|
+ .buttonStyle(.plain)
|
|
|
+ .foregroundStyle(.white.opacity(0.8))
|
|
|
+ }
|
|
|
+
|
|
|
+ Text("Selected plan: \(selectedPlan == .perpetual ? "Perpetual" : "Yearly")")
|
|
|
+ .font(.system(size: 13, weight: .medium))
|
|
|
+ .foregroundStyle(.white.opacity(0.8))
|
|
|
+
|
|
|
+ VStack(spacing: 12) {
|
|
|
+ checkoutField(title: "Cardholder Name", text: $cardholderName, placeholder: "Name on card")
|
|
|
+ checkoutField(title: "Card Number", text: $cardNumber, placeholder: "1234 5678 9012 3456")
|
|
|
+
|
|
|
+ HStack(spacing: 12) {
|
|
|
+ checkoutField(title: "Expiry (MM/YY)", text: $expiry, placeholder: "08/28")
|
|
|
+ checkoutField(title: "CVV", text: $cvv, placeholder: "123")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Toggle("Save card for future purchases", isOn: $saveCard)
|
|
|
+ .toggleStyle(.checkbox)
|
|
|
+ .foregroundStyle(.white.opacity(0.88))
|
|
|
+
|
|
|
+ HStack {
|
|
|
+ Text("Total")
|
|
|
+ .foregroundStyle(.white.opacity(0.85))
|
|
|
+ Spacer()
|
|
|
+ Text(amountText)
|
|
|
+ .font(.system(size: 15, weight: .bold))
|
|
|
+ .foregroundStyle(.white)
|
|
|
+ }
|
|
|
+ .padding(12)
|
|
|
+ .background(
|
|
|
+ RoundedRectangle(cornerRadius: 10, style: .continuous)
|
|
|
+ .fill(Color.white.opacity(0.08))
|
|
|
+ )
|
|
|
+
|
|
|
+ Button(action: onConfirm) {
|
|
|
+ Text("Pay \(amountText)")
|
|
|
+ .font(.system(size: 15, weight: .bold))
|
|
|
+ .foregroundStyle(.white)
|
|
|
+ .frame(maxWidth: .infinity)
|
|
|
+ .padding(.vertical, 12)
|
|
|
+ .background(
|
|
|
+ RoundedRectangle(cornerRadius: 12, style: .continuous)
|
|
|
+ .fill(canSubmit ? Color.blue.opacity(0.95) : Color.gray.opacity(0.6))
|
|
|
+ )
|
|
|
+ }
|
|
|
+ .buttonStyle(.plain)
|
|
|
+ .disabled(!canSubmit)
|
|
|
+
|
|
|
+ Spacer(minLength: 0)
|
|
|
+ }
|
|
|
+ .padding(20)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private func checkoutField(title: String, text: Binding<String>, placeholder: String) -> some View {
|
|
|
+ VStack(alignment: .leading, spacing: 6) {
|
|
|
+ Text(title)
|
|
|
+ .font(.system(size: 12, weight: .semibold))
|
|
|
+ .foregroundStyle(.white.opacity(0.85))
|
|
|
+ TextField(placeholder, text: text)
|
|
|
+ .textFieldStyle(.plain)
|
|
|
+ .foregroundStyle(.white)
|
|
|
+ .padding(.horizontal, 10)
|
|
|
+ .padding(.vertical, 9)
|
|
|
+ .background(
|
|
|
+ RoundedRectangle(cornerRadius: 10, style: .continuous)
|
|
|
+ .fill(Color.white.opacity(0.1))
|
|
|
+ )
|
|
|
+ .overlay(
|
|
|
+ RoundedRectangle(cornerRadius: 10, style: .continuous)
|
|
|
+ .stroke(Color.white.opacity(0.13), lineWidth: 1)
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|