vOOice/VoiceInk/Views/Metrics/DashboardPromotionsSection.swift
2025-10-29 20:51:31 +05:45

173 lines
6.5 KiB
Swift

import SwiftUI
import AppKit
struct DashboardPromotionsSection: View {
let licenseState: LicenseViewModel.LicenseState
private var shouldShowUpgradePromotion: Bool {
guard case .trial(let daysRemaining) = licenseState else { return false }
return daysRemaining <= 9
}
private var shouldShowAffiliatePromotion: Bool {
if case .licensed = licenseState {
return true
}
return false
}
private var shouldShowPromotions: Bool {
shouldShowUpgradePromotion || shouldShowAffiliatePromotion
}
var body: some View {
if shouldShowPromotions {
HStack(alignment: .top, spacing: 18) {
if shouldShowUpgradePromotion {
DashboardPromotionCard(
badge: "30% OFF",
title: "Share VoiceInk, Save 30%",
message: "Tell your audience about VoiceInk on social and unlock a 30% discount on VoiceInk Pro when they upgrade.",
gradient: LinearGradient(
colors: [
Color(red: 0.08, green: 0.48, blue: 0.85),
Color(red: 0.05, green: 0.18, blue: 0.42)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
),
accentSymbol: "megaphone.fill",
glowColor: Color(red: 0.08, green: 0.48, blue: 0.85),
actionTitle: "Share & Unlock",
actionIcon: "arrow.up.right",
action: openSocialShare
)
.frame(maxWidth: .infinity)
}
if shouldShowAffiliatePromotion {
DashboardPromotionCard(
badge: "AFFILIATE 30%",
title: "Earn With The VoiceInk Affiliate Program",
message: "Share VoiceInk with friends or your audience and receive 30% on every referral that upgrades.",
gradient: LinearGradient(
colors: [
Color(red: 0.08, green: 0.48, blue: 0.85),
Color(red: 0.05, green: 0.18, blue: 0.42)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
),
accentSymbol: "link.badge.plus",
glowColor: Color(red: 0.08, green: 0.48, blue: 0.85),
actionTitle: "Explore Affiliate",
actionIcon: "arrow.up.right",
action: openAffiliateProgram
)
.frame(maxWidth: .infinity)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
} else {
EmptyView()
}
}
private func openSocialShare() {
if let url = URL(string: "https://tryvoiceink.com/social-share") {
NSWorkspace.shared.open(url)
}
}
private func openAffiliateProgram() {
if let url = URL(string: "https://tryvoiceink.com/affiliate") {
NSWorkspace.shared.open(url)
}
}
}
private struct DashboardPromotionCard: View {
let badge: String
let title: String
let message: String
let gradient: LinearGradient
let accentSymbol: String
let glowColor: Color
let actionTitle: String
let actionIcon: String
let action: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: 18) {
HStack(alignment: .top) {
Text(badge.uppercased())
.font(.system(size: 11, weight: .heavy))
.tracking(0.8)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(.white.opacity(0.2))
.clipShape(Capsule())
.foregroundColor(.white)
Spacer()
Image(systemName: accentSymbol)
.font(.system(size: 20, weight: .bold))
.foregroundColor(.white.opacity(0.85))
.padding(10)
.background(.white.opacity(0.18))
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
}
Text(title)
.font(.system(size: 21, weight: .heavy, design: .rounded))
.foregroundColor(.white)
.fixedSize(horizontal: false, vertical: true)
Text(message)
.font(.system(size: 13.5, weight: .medium))
.foregroundColor(.white.opacity(0.85))
.fixedSize(horizontal: false, vertical: true)
Button(action: action) {
HStack(spacing: 6) {
Text(actionTitle)
Image(systemName: actionIcon)
}
.font(.system(size: 13, weight: .semibold))
.padding(.horizontal, 16)
.padding(.vertical, 9)
.background(.white.opacity(0.22))
.clipShape(Capsule())
.foregroundColor(.white)
}
.buttonStyle(.plain)
}
.padding(24)
.frame(maxWidth: .infinity, minHeight: 200, alignment: .topLeading)
.background(
RoundedRectangle(cornerRadius: 28, style: .continuous)
.fill(gradient)
.overlay {
ZStack {
Circle()
.fill(.white.opacity(0.12))
.frame(width: 140, height: 140)
.offset(x: 60, y: -60)
Circle()
.strokeBorder(.white.opacity(0.15), lineWidth: 1)
.frame(width: 170, height: 170)
.offset(x: -40, y: 70)
}
.clipped()
}
)
.clipShape(RoundedRectangle(cornerRadius: 28, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: 28, style: .continuous)
.stroke(.white.opacity(0.08), lineWidth: 1)
)
.shadow(color: glowColor.opacity(0.28), radius: 24, x: 0, y: 14)
}
}