231 lines
7.0 KiB
Swift
231 lines
7.0 KiB
Swift
import SwiftUI
|
|
|
|
struct OnboardingView: View {
|
|
@Binding var onboardingCompleted: Bool
|
|
|
|
@State private var currentPage = 0
|
|
|
|
private let pages: [OnboardingPage] = [
|
|
OnboardingPage(
|
|
icon: "chart.pie.fill",
|
|
title: "Track Your Investments",
|
|
description: "Monitor all your investment sources in one place. Stocks, bonds, real estate, crypto, and more.",
|
|
color: .appPrimary
|
|
),
|
|
OnboardingPage(
|
|
icon: "chart.line.uptrend.xyaxis",
|
|
title: "Visualize Your Growth",
|
|
description: "Beautiful charts show your portfolio evolution, allocation, and performance over time.",
|
|
color: .positiveGreen
|
|
),
|
|
OnboardingPage(
|
|
icon: "bell.badge.fill",
|
|
title: "Never Miss an Update",
|
|
description: "Set reminders to track your investments regularly. Monthly, quarterly, or custom schedules.",
|
|
color: .appWarning
|
|
),
|
|
OnboardingPage(
|
|
icon: "icloud.fill",
|
|
title: "Sync Everywhere",
|
|
description: "Your data syncs automatically via iCloud across all your Apple devices.",
|
|
color: .appSecondary
|
|
)
|
|
]
|
|
|
|
var body: some View {
|
|
VStack {
|
|
// Pages
|
|
TabView(selection: $currentPage) {
|
|
ForEach(0..<pages.count, id: \.self) { index in
|
|
OnboardingPageView(page: pages[index])
|
|
.tag(index)
|
|
}
|
|
}
|
|
.tabViewStyle(.page(indexDisplayMode: .never))
|
|
.animation(.easeInOut, value: currentPage)
|
|
|
|
// Page indicators
|
|
HStack(spacing: 8) {
|
|
ForEach(0..<pages.count, id: \.self) { index in
|
|
Circle()
|
|
.fill(currentPage == index ? Color.appPrimary : Color.gray.opacity(0.3))
|
|
.frame(width: 8, height: 8)
|
|
.animation(.easeInOut, value: currentPage)
|
|
}
|
|
}
|
|
.padding(.bottom, 20)
|
|
|
|
// Buttons
|
|
VStack(spacing: 12) {
|
|
if currentPage < pages.count - 1 {
|
|
Button {
|
|
withAnimation {
|
|
currentPage += 1
|
|
}
|
|
} label: {
|
|
Text("Continue")
|
|
.font(.headline)
|
|
.foregroundColor(.white)
|
|
.frame(maxWidth: .infinity)
|
|
.padding()
|
|
.background(Color.appPrimary)
|
|
.cornerRadius(AppConstants.UI.cornerRadius)
|
|
}
|
|
|
|
Button {
|
|
completeOnboarding()
|
|
} label: {
|
|
Text("Skip")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
} else {
|
|
Button {
|
|
completeOnboarding()
|
|
} label: {
|
|
Text("Get Started")
|
|
.font(.headline)
|
|
.foregroundColor(.white)
|
|
.frame(maxWidth: .infinity)
|
|
.padding()
|
|
.background(Color.appPrimary)
|
|
.cornerRadius(AppConstants.UI.cornerRadius)
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
.padding(.bottom, 40)
|
|
}
|
|
.background(Color(.systemBackground))
|
|
}
|
|
|
|
private func completeOnboarding() {
|
|
// Create default categories
|
|
let categoryRepository = CategoryRepository()
|
|
categoryRepository.createDefaultCategoriesIfNeeded()
|
|
|
|
// Mark onboarding as complete
|
|
let context = CoreDataStack.shared.viewContext
|
|
AppSettings.markOnboardingComplete(in: context)
|
|
|
|
// Log analytics
|
|
FirebaseService.shared.logOnboardingCompleted(stepCount: pages.count)
|
|
|
|
// Request notification permission
|
|
Task {
|
|
await NotificationService.shared.requestAuthorization()
|
|
}
|
|
|
|
withAnimation {
|
|
onboardingCompleted = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Onboarding Page Model
|
|
|
|
struct OnboardingPage {
|
|
let icon: String
|
|
let title: String
|
|
let description: String
|
|
let color: Color
|
|
}
|
|
|
|
// MARK: - Onboarding Page View
|
|
|
|
struct OnboardingPageView: View {
|
|
let page: OnboardingPage
|
|
|
|
var body: some View {
|
|
VStack(spacing: 32) {
|
|
Spacer()
|
|
|
|
// Icon
|
|
ZStack {
|
|
Circle()
|
|
.fill(page.color.opacity(0.15))
|
|
.frame(width: 140, height: 140)
|
|
|
|
Circle()
|
|
.fill(page.color.opacity(0.3))
|
|
.frame(width: 100, height: 100)
|
|
|
|
Image(systemName: page.icon)
|
|
.font(.system(size: 50))
|
|
.foregroundColor(page.color)
|
|
}
|
|
|
|
// Content
|
|
VStack(spacing: 16) {
|
|
Text(page.title)
|
|
.font(.title.weight(.bold))
|
|
.multilineTextAlignment(.center)
|
|
|
|
Text(page.description)
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal, 40)
|
|
}
|
|
|
|
Spacer()
|
|
Spacer()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - First Launch Welcome
|
|
|
|
struct WelcomeView: View {
|
|
@Binding var showWelcome: Bool
|
|
let userName: String?
|
|
|
|
var body: some View {
|
|
VStack(spacing: 24) {
|
|
Spacer()
|
|
|
|
Image(systemName: "hand.wave.fill")
|
|
.font(.system(size: 60))
|
|
.foregroundColor(.appPrimary)
|
|
|
|
VStack(spacing: 8) {
|
|
if let name = userName {
|
|
Text("Welcome back, \(name)!")
|
|
.font(.title.weight(.bold))
|
|
} else {
|
|
Text("Welcome!")
|
|
.font(.title.weight(.bold))
|
|
}
|
|
|
|
Text("Your investment data has been synced from iCloud.")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
Button {
|
|
withAnimation {
|
|
showWelcome = false
|
|
}
|
|
} label: {
|
|
Text("Continue")
|
|
.font(.headline)
|
|
.foregroundColor(.white)
|
|
.frame(maxWidth: .infinity)
|
|
.padding()
|
|
.background(Color.appPrimary)
|
|
.cornerRadius(AppConstants.UI.cornerRadius)
|
|
}
|
|
.padding(.horizontal, 24)
|
|
.padding(.bottom, 40)
|
|
}
|
|
.background(Color(.systemBackground))
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
OnboardingView(onboardingCompleted: .constant(false))
|
|
}
|