InvestmentTrackerApp/InvestmentTracker/Views/Onboarding/OnboardingView.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))
}