import SwiftUI struct GoalShareCardView: View { let name: String let progress: Double let currentValue: Decimal let targetValue: Decimal var targetDate: Date? = nil var estimatedCompletionDate: Date? = nil var privacyMode: Bool = false var qrCodeImage: UIImage? = nil private var hasExtraContent: Bool { targetDate != nil || estimatedCompletionDate != nil } private var cardHeight: CGFloat { let base: CGFloat = hasExtraContent ? 340 : 290 return privacyMode ? base + 10 : base } private var displayCurrentValue: String { privacyMode ? "***" : currentValue.currencyString } private var progressText: String { let percent = Int((progress * 100).rounded()) return "\(percent)%" } var body: some View { VStack(alignment: .leading, spacing: 14) { // Header with goal name VStack(alignment: .leading, spacing: 4) { Text("Goal Progress") .font(.caption.weight(.semibold)) .foregroundColor(.white.opacity(0.8)) Text(name) .font(.title2.weight(.bold)) .foregroundColor(.white) } GoalProgressBar( progress: progress, tint: .white.opacity(0.9), background: .white.opacity(0.2), iconName: "sparkles", iconColor: .white ) .frame(height: 10) Text("\(progressText) complete") .font(.caption.weight(.semibold)) .foregroundColor(.white.opacity(0.85)) if privacyMode { HStack { Text("Progress") .font(.subheadline.weight(.semibold)) .foregroundColor(.white.opacity(0.9)) Spacer() Text(progressText) .font(.headline.weight(.bold)) .foregroundColor(.white) } } else { HStack { Text(displayCurrentValue) .font(.headline) .foregroundColor(.white) Spacer() Text("of \(targetValue.currencyString)") .font(.subheadline.weight(.medium)) .foregroundColor(.white.opacity(0.8)) } } if let targetDate { HStack(spacing: 6) { Image(systemName: "calendar") .font(.caption) Text("Target: \(targetDate.mediumDateString)") .font(.caption.weight(.medium)) } .foregroundColor(.white.opacity(0.85)) } if let estimatedCompletionDate { HStack(spacing: 6) { Image(systemName: "chart.line.uptrend.xyaxis") .font(.caption) Text("Est. completion: \(estimatedCompletionDate.mediumDateString)") .font(.caption.weight(.medium)) } .foregroundColor(.white.opacity(0.85)) } if privacyMode { HStack(spacing: 6) { Image(systemName: "eye.slash") Text("Privacy mode enabled") } .font(.caption.weight(.semibold)) .foregroundColor(.white.opacity(0.7)) } Spacer(minLength: 0) // Branding footer with QR code VStack(spacing: 10) { // Divider line Rectangle() .fill(Color.white.opacity(0.2)) .frame(height: 1) HStack(spacing: 12) { // App icon and branding Image("BrandMark") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 40, height: 40) .clipShape(RoundedRectangle(cornerRadius: 9, style: .continuous)) VStack(alignment: .leading, spacing: 2) { Text("Powered by") .font(.caption2.weight(.medium)) .foregroundColor(.white.opacity(0.7)) Text("Portfolio Journal") .font(.subheadline.weight(.bold)) .foregroundColor(.white) } Spacer() // QR Code if let qrCodeImage { VStack(spacing: 4) { Image(uiImage: qrCodeImage) .interpolation(.none) .resizable() .frame(width: 50, height: 50) .background(Color.white) .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous)) Text("Scan to download") .font(.system(size: 7, weight: .medium)) .foregroundColor(.white.opacity(0.8)) } } else { // Fallback if QR code generation fails VStack(spacing: 2) { Image(systemName: "qrcode") .font(.system(size: 28)) Text("App Store") .font(.caption2.weight(.semibold)) } .foregroundColor(.white.opacity(0.9)) } } } } .padding(20) .frame(width: 320, height: cardHeight) .background( LinearGradient( colors: [Color.appPrimary, Color.appSecondary], startPoint: .topLeading, endPoint: .bottomTrailing ) ) .clipShape(RoundedRectangle(cornerRadius: 24, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 24, style: .continuous) .stroke(Color.white.opacity(0.2), lineWidth: 1) ) } } #Preview { GoalShareCardView( name: "1M Goal", progress: 0.42, currentValue: 420_000, targetValue: 1_000_000 ) } #Preview("With Dates") { GoalShareCardView( name: "1M Goal", progress: 0.42, currentValue: 420_000, targetValue: 1_000_000, targetDate: Date().addingTimeInterval(365 * 24 * 60 * 60), estimatedCompletionDate: Date().addingTimeInterval(300 * 24 * 60 * 60) ) } #Preview("Privacy Mode") { GoalShareCardView( name: "1M Goal", progress: 0.42, currentValue: 420_000, targetValue: 1_000_000, privacyMode: true ) }