Xcode 27: compila sin warnings y migra API deprecadas de SwiftUI
- Imports de CoreData que faltaban (MemberImportVisibility) en AccountsView, ImportDataView, ChartsContainerView y AddSourceView. - Aislamiento MainActor: IsolatedDefaultValues en el target de la app, LinearGradient @MainActor, WatchSyncMessage nonisolated, closures de ReviewPromptService/MonthlyCheckInStore/AppIntents. - Warnings de valores sin usar (SnapshotGapDetector, DashboardLayoutStore, OnboardingView, GoalEditorView, CoreDataStack, SettingsViewModel). - foregroundColor→foregroundStyle, cornerRadius→clipShape, navigationBarLeading/Trailing→topBarLeading/Trailing, Task.sleep(nanoseconds:)→sleep(for:), NavigationLink(isActive:)→ navigationDestination(isPresented:). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1u4K16xy7eQVtgsYNZ9Vn
This commit is contained in:
@@ -21,11 +21,11 @@ struct AccountEditorView: View {
|
||||
if isEditingDefaultAccount {
|
||||
HStack {
|
||||
Text(Account.defaultAccountName)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
Image(systemName: "lock.fill")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
} else {
|
||||
TextField("Account name", text: $name)
|
||||
@@ -40,7 +40,7 @@ struct AccountEditorView: View {
|
||||
} footer: {
|
||||
if let errorMessage {
|
||||
Text(errorMessage)
|
||||
.foregroundColor(.negativeRed)
|
||||
.foregroundStyle(Color.negativeRed)
|
||||
} else if isEditingDefaultAccount {
|
||||
Text("The Default account name cannot be changed.")
|
||||
}
|
||||
@@ -62,10 +62,10 @@ struct AccountEditorView: View {
|
||||
.navigationTitle(account == nil ? "New Account" : "Edit Account")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Save") { saveAccount() }
|
||||
.disabled(!isValid)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
|
||||
struct AccountsView: View {
|
||||
@EnvironmentObject private var iapService: IAPService
|
||||
@@ -33,17 +34,17 @@ struct AccountsView: View {
|
||||
if account.isDefaultAccount {
|
||||
Image(systemName: "star.fill")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.appWarning)
|
||||
.foregroundStyle(Color.appWarning)
|
||||
}
|
||||
}
|
||||
Text(account.currencyCode ?? AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currency)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
if accountStore.selectedAccount?.safeId == account.safeId && !accountStore.showAllAccounts {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +93,7 @@ struct AccountsView: View {
|
||||
Text("The Default account cannot be deleted. You must keep at least one account.")
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button {
|
||||
if accountStore.canAddAccount() {
|
||||
showingAddAccount = true
|
||||
|
||||
@@ -10,7 +10,7 @@ struct LoadingView: View {
|
||||
|
||||
Text(message)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(Color(.systemBackground))
|
||||
@@ -30,14 +30,14 @@ struct AppLaunchLoadingView: View {
|
||||
.frame(width: 140, height: 140)
|
||||
.padding(16)
|
||||
.background(Color.appPrimary.opacity(0.08))
|
||||
.cornerRadius(28)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 28))
|
||||
|
||||
ProgressView()
|
||||
.scaleEffect(1.2)
|
||||
|
||||
Text(messageKey)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
@@ -82,27 +82,27 @@ struct SkeletonCardView: View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
SkeletonView()
|
||||
.frame(width: 100, height: 16)
|
||||
.cornerRadius(4)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
|
||||
SkeletonView()
|
||||
.frame(height: 32)
|
||||
.cornerRadius(4)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
|
||||
HStack {
|
||||
SkeletonView()
|
||||
.frame(width: 80, height: 14)
|
||||
.cornerRadius(4)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
|
||||
Spacer()
|
||||
|
||||
SkeletonView()
|
||||
.frame(width: 60, height: 14)
|
||||
.cornerRadius(4)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,14 +119,14 @@ struct EmptyStateView: View {
|
||||
VStack(spacing: 20) {
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 60))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Text(title)
|
||||
.font(.title2.weight(.semibold))
|
||||
|
||||
Text(message)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
|
||||
@@ -134,11 +134,11 @@ struct EmptyStateView: View {
|
||||
Button(action: action) {
|
||||
Text(actionTitle)
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.padding()
|
||||
.frame(maxWidth: 200)
|
||||
.background(Color.appPrimary)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,14 +156,14 @@ struct ErrorView: View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(.appWarning)
|
||||
.foregroundStyle(Color.appWarning)
|
||||
|
||||
Text("Something went wrong")
|
||||
.font(.headline)
|
||||
|
||||
Text(message)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
|
||||
@@ -174,8 +174,8 @@ struct ErrorView: View {
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 10)
|
||||
.background(Color.appPrimary)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(20)
|
||||
.foregroundStyle(.white)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ struct SuccessView: View {
|
||||
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.font(.system(size: 60))
|
||||
.foregroundColor(.positiveGreen)
|
||||
.foregroundStyle(Color.positiveGreen)
|
||||
}
|
||||
|
||||
Text(title)
|
||||
@@ -207,7 +207,7 @@ struct SuccessView: View {
|
||||
|
||||
Text(message)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
|
||||
@@ -215,11 +215,11 @@ struct SuccessView: View {
|
||||
Button(action: action) {
|
||||
Text("Continue")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.padding()
|
||||
.frame(maxWidth: 200)
|
||||
.background(Color.appPrimary)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,14 +255,14 @@ struct ToastView: View {
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: type.icon)
|
||||
.foregroundColor(type.color)
|
||||
.foregroundStyle(type.color)
|
||||
|
||||
Text(message)
|
||||
.font(.subheadline)
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.1), radius: 10, y: 5)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ struct ProgressRing: View {
|
||||
.animation(.easeOut(duration: 0.6), value: clamped)
|
||||
Text("\(Int((clamped * 100).rounded()))%")
|
||||
.font(.system(size: size * 0.26, weight: .bold, design: .rounded))
|
||||
.foregroundColor(tint)
|
||||
.foregroundStyle(tint)
|
||||
.minimumScaleFactor(0.7)
|
||||
}
|
||||
.frame(width: size, height: size)
|
||||
|
||||
@@ -17,7 +17,7 @@ struct CategoryBreakdownCard: View {
|
||||
} label: {
|
||||
Text("See All")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ struct CategoryBreakdownCard: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ struct CategoryRowView: View {
|
||||
|
||||
Image(systemName: category.icon)
|
||||
.font(.system(size: 14))
|
||||
.foregroundColor(Color(hex: category.colorHex) ?? .gray)
|
||||
.foregroundStyle(Color(hex: category.colorHex) ?? .gray)
|
||||
}
|
||||
|
||||
// Name and percentage
|
||||
@@ -65,12 +65,12 @@ struct CategoryRowView: View {
|
||||
|
||||
Text(category.formattedPercentage)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
if let target = targetPercentage {
|
||||
Text("Target \(String(format: "%.0f%%", target)) | Drift \(driftText ?? "")")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,10 +84,10 @@ struct CategoryRowView: View {
|
||||
HStack(spacing: 4) {
|
||||
Text("CAGR")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(category.metrics.formattedCAGR)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(category.metrics.cagr >= 0 ? .positiveGreen : .negativeRed)
|
||||
.foregroundStyle(category.metrics.cagr >= 0 ? Color.positiveGreen : Color.negativeRed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ struct CategoryProgressBar: View {
|
||||
|
||||
Text(category.formattedPercentage)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
GeometryReader { geometry in
|
||||
@@ -130,12 +130,12 @@ struct CategoryProgressBar: View {
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.1))
|
||||
.frame(height: 6)
|
||||
.cornerRadius(3)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 3))
|
||||
|
||||
Rectangle()
|
||||
.fill(Color(hex: category.colorHex) ?? .gray)
|
||||
.frame(width: geometry.size.width * progress, height: 6)
|
||||
.cornerRadius(3)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 3))
|
||||
}
|
||||
}
|
||||
.frame(height: 6)
|
||||
@@ -166,7 +166,7 @@ struct SimpleCategoryList: View {
|
||||
|
||||
Text("(\(category.formattedPercentage))")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,27 +13,27 @@ struct DataGapsBanner: View {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.font(.title3)
|
||||
.foregroundColor(.orange)
|
||||
.foregroundStyle(.orange)
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(String(format: String(localized: "gaps_banner_title"), totalMissingMonths))
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.primary)
|
||||
.foregroundStyle(.primary)
|
||||
.multilineTextAlignment(.leading)
|
||||
Text("Fill in missing data")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(14)
|
||||
.background(Color(.secondarySystemGroupedBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius, style: .continuous)
|
||||
.stroke(Color.orange.opacity(0.35), lineWidth: 1)
|
||||
@@ -44,7 +44,7 @@ struct DataGapsBanner: View {
|
||||
Button(action: onDismiss) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.body)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(6)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
@@ -89,7 +89,7 @@ struct DataGapsSheet: View {
|
||||
.navigationTitle("Missing Data")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Done") { dismiss() }
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ struct DataGapsSheet: View {
|
||||
Spacer()
|
||||
Text(String(format: String(localized: "gaps_missing_count"), gap.missingMonths))
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.orange)
|
||||
.foregroundStyle(.orange)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 3)
|
||||
.background(Color.orange.opacity(0.12))
|
||||
@@ -118,7 +118,7 @@ struct DataGapsSheet: View {
|
||||
|
||||
Text(rangeLabel(gap))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
if let source = sourceProvider(gap.sourceId),
|
||||
let firstMissing = gap.missingMonthDates.first {
|
||||
|
||||
@@ -147,7 +147,7 @@ struct EvolutionChartCard: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
.contentShape(Rectangle())
|
||||
// simultaneousGesture so the swipe is recognized even over the chart,
|
||||
@@ -206,16 +206,16 @@ struct EvolutionChartCard: View {
|
||||
.frame(width: 72, height: 72)
|
||||
Image(systemName: page.chartsType.icon)
|
||||
.font(.system(size: 28))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
Image(systemName: "lock.circle.fill")
|
||||
.font(.system(size: 22))
|
||||
.foregroundColor(.appWarning)
|
||||
.foregroundStyle(Color.appWarning)
|
||||
.background(Circle().fill(Color(.systemBackground)))
|
||||
.offset(x: 26, y: 24)
|
||||
}
|
||||
Text(String(localized: "home_chart_locked_sub"))
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
Button {
|
||||
FirebaseService.shared.logPaywallShown(trigger: "home_charts")
|
||||
@@ -226,7 +226,7 @@ struct EvolutionChartCard: View {
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 10)
|
||||
.background(Color.appPrimary)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
@@ -246,7 +246,7 @@ struct EvolutionChartCard: View {
|
||||
showGoalLines.toggle()
|
||||
} label: {
|
||||
Image(systemName: showGoalLines ? "target" : "slash.circle")
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.accessibilityLabel(showGoalLines ? "Hide goals" : "Show goals")
|
||||
|
||||
@@ -256,7 +256,7 @@ struct EvolutionChartCard: View {
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Text(selected.date.monthYearString)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,7 +303,7 @@ struct EvolutionChartCard: View {
|
||||
Text(item.category).font(.caption)
|
||||
Spacer()
|
||||
Text(String(format: "%.1f%%", pct)).font(.caption.weight(.medium))
|
||||
Text(item.value.compactCurrencyString).font(.caption).foregroundColor(.secondary)
|
||||
Text(item.value.compactCurrencyString).font(.caption).foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,7 +325,7 @@ struct EvolutionChartCard: View {
|
||||
} else {
|
||||
Text("Not enough data to display chart")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(height: 200)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
@@ -449,7 +449,7 @@ struct EvolutionChartCard: View {
|
||||
.annotation(position: .topTrailing) {
|
||||
Text(goal.name)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ struct GuidedCheckInView: View {
|
||||
if let category = source.category?.name {
|
||||
Text(category)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,10 +134,10 @@ struct GuidedCheckInView: View {
|
||||
VStack(spacing: 2) {
|
||||
Text(String(localized: "guided_previous_value"))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(source.latestValue.currencyString)
|
||||
.font(.title3.weight(.semibold))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ struct GuidedCheckInView: View {
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 24)
|
||||
.background(Color(.secondarySystemGroupedBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.padding(.horizontal, 32)
|
||||
|
||||
liveDelta(for: source)
|
||||
@@ -202,7 +202,7 @@ struct GuidedCheckInView: View {
|
||||
} label: {
|
||||
Text(String(localized: "guided_skip"))
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.accessibilityIdentifier("guided_skip_btn")
|
||||
}
|
||||
@@ -224,7 +224,7 @@ struct GuidedCheckInView: View {
|
||||
Text("\(delta.currencyString) (\(String(format: "%+.1f%%", pct)))")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
}
|
||||
.foregroundColor(positive ? .positiveGreen : .negativeRed)
|
||||
.foregroundStyle(positive ? Color.positiveGreen : Color.negativeRed)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background((positive ? Color.positiveGreen : Color.negativeRed).opacity(0.12))
|
||||
@@ -247,7 +247,7 @@ struct GuidedCheckInView: View {
|
||||
.font(.title2.weight(.bold))
|
||||
Text(String(localized: "guided_reflection_subtitle"))
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
HStack(spacing: 10) {
|
||||
@@ -272,8 +272,8 @@ struct GuidedCheckInView: View {
|
||||
? Color.appPrimary.opacity(0.15)
|
||||
: Color(.secondarySystemGroupedBackground)
|
||||
)
|
||||
.foregroundColor(mood == candidate ? .appPrimary : .primary)
|
||||
.cornerRadius(12)
|
||||
.foregroundStyle(mood == candidate ? Color.appPrimary : .primary)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.strokeBorder(mood == candidate ? Color.appPrimary : .clear, lineWidth: 1.5)
|
||||
@@ -290,7 +290,7 @@ struct GuidedCheckInView: View {
|
||||
.focused($noteFocused)
|
||||
.padding(14)
|
||||
.background(Color(.secondarySystemGroupedBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.padding(.horizontal, 24)
|
||||
|
||||
Spacer()
|
||||
@@ -319,7 +319,7 @@ struct GuidedCheckInView: View {
|
||||
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
.font(.system(size: 64))
|
||||
.foregroundColor(.positiveGreen)
|
||||
.foregroundStyle(Color.positiveGreen)
|
||||
.symbolEffect(.bounce, value: step)
|
||||
|
||||
Text(String(format: String(localized: "guided_done_title"), monthLabel))
|
||||
@@ -335,7 +335,7 @@ struct GuidedCheckInView: View {
|
||||
let pct = NSDecimalNumber(decimal: delta / totals.previous).doubleValue * 100
|
||||
Text("\(delta.currencyString) (\(String(format: "%+.1f%%", pct)))")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(delta >= 0 ? .positiveGreen : .negativeRed)
|
||||
.foregroundStyle(delta >= 0 ? Color.positiveGreen : Color.negativeRed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,7 +347,7 @@ struct GuidedCheckInView: View {
|
||||
Text(String(format: String(localized: "guided_streak"), streak))
|
||||
}
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.orange)
|
||||
.foregroundStyle(.orange)
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 8)
|
||||
.background(Color.orange.opacity(0.12))
|
||||
|
||||
@@ -21,7 +21,7 @@ struct InsightsRow: View {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: insight.systemImage)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(insight.accentColor)
|
||||
.foregroundStyle(insight.accentColor)
|
||||
.frame(width: 30, height: 30)
|
||||
.background(insight.accentColor.opacity(0.12))
|
||||
.clipShape(Circle())
|
||||
@@ -29,15 +29,15 @@ struct InsightsRow: View {
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
Text(insight.title)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(insight.value)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.primary)
|
||||
.foregroundStyle(.primary)
|
||||
.lineLimit(2)
|
||||
if let detail = insight.detail {
|
||||
Text(detail)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ struct InsightsRow: View {
|
||||
}
|
||||
.padding(14)
|
||||
.background(Color(.secondarySystemGroupedBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.id(insight.id)
|
||||
|
||||
@@ -116,7 +116,7 @@ struct MonthlyCheckInView: View {
|
||||
} label: {
|
||||
Image(systemName: "chevron.left")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
|
||||
Text(monthLabel)
|
||||
@@ -127,13 +127,13 @@ struct MonthlyCheckInView: View {
|
||||
} label: {
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(canGoToNextMonth ? .appPrimary : .secondary.opacity(0.3))
|
||||
.foregroundStyle(canGoToNextMonth ? Color.appPrimary : .secondary.opacity(0.3))
|
||||
}
|
||||
.disabled(!canGoToNextMonth)
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button {
|
||||
shareMonthlyCheckIn()
|
||||
} label: {
|
||||
@@ -230,11 +230,11 @@ struct MonthlyCheckInView: View {
|
||||
)
|
||||
)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
Text("Start your first check-in anytime.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,10 +242,10 @@ struct MonthlyCheckInView: View {
|
||||
if let completed = MonthlyCheckInStore.completionDate(for: referenceDate) {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.positiveGreen)
|
||||
.foregroundStyle(Color.positiveGreen)
|
||||
Text("Completed \(completed.friendlyDescription)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,12 +272,12 @@ struct MonthlyCheckInView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 10)
|
||||
.background(isCompleted ? Color.appSecondary.opacity(0.1) : Color.appPrimary.opacity(0.1))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ struct MonthlyCheckInView: View {
|
||||
Spacer()
|
||||
Text("Optional")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
@@ -326,7 +326,7 @@ struct MonthlyCheckInView: View {
|
||||
} label: {
|
||||
Image(systemName: value <= starRating ? "star.fill" : "star")
|
||||
.font(.title3)
|
||||
.foregroundColor(value <= starRating ? .appSecondary : .secondary)
|
||||
.foregroundStyle(value <= starRating ? Color.appSecondary : .secondary)
|
||||
.padding(8)
|
||||
.background(
|
||||
Circle()
|
||||
@@ -346,7 +346,7 @@ struct MonthlyCheckInView: View {
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 6)
|
||||
.background(Color.gray.opacity(0.12))
|
||||
.cornerRadius(AppConstants.UI.smallCornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -373,7 +373,7 @@ struct MonthlyCheckInView: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ struct MonthlyCheckInView: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Starting")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(viewModel.monthlySummary.formattedStartingValue)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
}
|
||||
@@ -394,7 +394,7 @@ struct MonthlyCheckInView: View {
|
||||
VStack(alignment: .trailing, spacing: 4) {
|
||||
Text("Ending")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(viewModel.monthlySummary.formattedEndingValue)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
}
|
||||
@@ -404,7 +404,7 @@ struct MonthlyCheckInView: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Contributions")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(viewModel.monthlySummary.formattedContributions)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
}
|
||||
@@ -412,16 +412,16 @@ struct MonthlyCheckInView: View {
|
||||
VStack(alignment: .trailing, spacing: 4) {
|
||||
Text("Net Performance")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("\(viewModel.monthlySummary.formattedNetPerformance) (\(viewModel.monthlySummary.formattedNetPerformancePercentage))")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(viewModel.monthlySummary.netPerformance >= 0 ? .positiveGreen : .negativeRed)
|
||||
.foregroundStyle(viewModel.monthlySummary.netPerformance >= 0 ? Color.positiveGreen : Color.negativeRed)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -489,7 +489,7 @@ struct MonthlyCheckInView: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
}
|
||||
@@ -497,11 +497,11 @@ struct MonthlyCheckInView: View {
|
||||
private func highlightRow(icon: String, iconColor: Color, label: String, name: String, percentage: Double, diff: Decimal, valueColor: Color) -> some View {
|
||||
HStack {
|
||||
Image(systemName: icon)
|
||||
.foregroundColor(iconColor)
|
||||
.foregroundStyle(iconColor)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(label)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(name)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
}
|
||||
@@ -509,10 +509,10 @@ struct MonthlyCheckInView: View {
|
||||
VStack(alignment: .trailing, spacing: 2) {
|
||||
Text(String(format: "%+.1f%%", percentage))
|
||||
.font(.subheadline.weight(.bold))
|
||||
.foregroundColor(valueColor)
|
||||
.foregroundStyle(valueColor)
|
||||
Text("(\(diff.compactCurrencyString))")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -529,18 +529,18 @@ struct MonthlyCheckInView: View {
|
||||
} label: {
|
||||
Label("Batch Update", systemImage: "square.and.pencil")
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
}
|
||||
Text("\(viewModel.sources.count)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
if viewModel.sources.isEmpty {
|
||||
Text("Add sources to start your monthly check-in.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
ForEach(viewModel.sources, id: \.objectID) { source in
|
||||
// Month-aware: "updated" means updated FOR THE VIEWED month.
|
||||
@@ -575,7 +575,7 @@ struct MonthlyCheckInView: View {
|
||||
.font(.subheadline.weight(.medium))
|
||||
Text(updatedThisCycle ? "Updated this cycle" : "Needs update")
|
||||
.font(.caption2)
|
||||
.foregroundColor(updatedThisCycle ? .positiveGreen : .secondary)
|
||||
.foregroundStyle(updatedThisCycle ? Color.positiveGreen : .secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -583,16 +583,16 @@ struct MonthlyCheckInView: View {
|
||||
if let diff = valueDiff, updatedThisCycle {
|
||||
Text(diff >= 0 ? "+\(diff.compactCurrencyString)" : diff.compactCurrencyString)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(diff >= 0 ? .positiveGreen : .negativeRed)
|
||||
.foregroundStyle(diff >= 0 ? Color.positiveGreen : Color.negativeRed)
|
||||
}
|
||||
|
||||
Text(latestSnapshot?.date.relativeDayDescription ?? String(localized: "date_never"))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
@@ -601,7 +601,7 @@ struct MonthlyCheckInView: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -614,7 +614,7 @@ struct MonthlyCheckInView: View {
|
||||
.frame(minHeight: 120)
|
||||
.padding(8)
|
||||
.background(Color.gray.opacity(0.08))
|
||||
.cornerRadius(12)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.focused($noteFocused)
|
||||
.onChange(of: monthlyNote) { _, newValue in
|
||||
MonthlyCheckInStore.setNote(newValue, for: referenceDate)
|
||||
@@ -629,7 +629,7 @@ struct MonthlyCheckInView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 8)
|
||||
.background(Color.appSecondary.opacity(0.12))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
@@ -642,13 +642,13 @@ struct MonthlyCheckInView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 8)
|
||||
.background(Color.appPrimary.opacity(0.12))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -660,7 +660,7 @@ struct MonthlyCheckInView: View {
|
||||
if viewModel.recentNotes.isEmpty {
|
||||
Text("No snapshot notes for this month.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
ForEach(viewModel.recentNotes, id: \.objectID) { snapshot in
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
@@ -668,10 +668,10 @@ struct MonthlyCheckInView: View {
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Text(snapshot.notes ?? "")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(snapshot.date.friendlyDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
if snapshot.id != viewModel.recentNotes.last?.id {
|
||||
@@ -682,7 +682,7 @@ struct MonthlyCheckInView: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -764,11 +764,11 @@ struct AchievementsView: View {
|
||||
)
|
||||
)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -785,7 +785,7 @@ struct AchievementsView: View {
|
||||
if let subtitle {
|
||||
Text(subtitle)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
if achievements.isEmpty {
|
||||
@@ -798,7 +798,7 @@ struct AchievementsView: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -810,7 +810,7 @@ struct AchievementsView: View {
|
||||
.frame(width: 42, height: 42)
|
||||
Image(systemName: status.achievement.icon)
|
||||
.font(.headline)
|
||||
.foregroundColor(isLocked ? .secondary : .appSecondary)
|
||||
.foregroundStyle(isLocked ? .secondary : Color.appSecondary)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
@@ -818,7 +818,7 @@ struct AchievementsView: View {
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Text(status.achievement.detail)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -826,12 +826,12 @@ struct AchievementsView: View {
|
||||
if isLocked {
|
||||
Image(systemName: "lock.fill")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
.background(isLocked ? Color.gray.opacity(0.08) : Color.appSecondary.opacity(0.12))
|
||||
.cornerRadius(AppConstants.UI.smallCornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,13 +865,13 @@ private extension MonthlyCheckInView {
|
||||
HStack(alignment: .center, spacing: 8) {
|
||||
Image(systemName: mood.iconName)
|
||||
.font(.body)
|
||||
.foregroundColor(isSelected ? moodColor(for: mood) : .secondary)
|
||||
.foregroundStyle(isSelected ? moodColor(for: mood) : .secondary)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(mood.title)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Text(mood.detail)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
@@ -880,7 +880,7 @@ private extension MonthlyCheckInView {
|
||||
RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius)
|
||||
.stroke(isSelected ? moodColor(for: mood) : Color.clear, lineWidth: 1)
|
||||
)
|
||||
.cornerRadius(AppConstants.UI.smallCornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius))
|
||||
}
|
||||
|
||||
func moodColor(for mood: MonthlyCheckInMood) -> Color {
|
||||
@@ -940,7 +940,7 @@ struct AchievementMilestoneBar: View {
|
||||
if status.isUnlocked {
|
||||
Image(systemName: "checkmark")
|
||||
.font(.system(size: 8, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
}
|
||||
.position(x: x, y: barY)
|
||||
@@ -989,10 +989,10 @@ struct BatchUpdateView: View {
|
||||
.navigationTitle("Batch Update")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Save") { saveAll() }
|
||||
.disabled(filledCount == 0)
|
||||
.fontWeight(.semibold)
|
||||
@@ -1030,33 +1030,33 @@ struct BatchUpdateView: View {
|
||||
if let prev = previousValue {
|
||||
Text(prev.currencyString)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text(symbol)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
TextField("Current value", text: valueBinding)
|
||||
.keyboardType(.decimalPad)
|
||||
}
|
||||
.padding(8)
|
||||
.background(Color.gray.opacity(0.08))
|
||||
.cornerRadius(8)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
|
||||
if isDetailed {
|
||||
HStack {
|
||||
Image(systemName: "plus.circle")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
TextField("Contribution this period (optional)", text: contributionBinding)
|
||||
.keyboardType(.decimalPad)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(8)
|
||||
.background(Color.appSecondary.opacity(0.06))
|
||||
.cornerRadius(8)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 2)
|
||||
|
||||
@@ -21,7 +21,7 @@ struct MonthlySummaryShareView: View {
|
||||
HStack {
|
||||
Text(monthLabel)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
.foregroundStyle(.white.opacity(0.85))
|
||||
.textCase(.uppercase)
|
||||
Spacer()
|
||||
if streak >= 2 {
|
||||
@@ -31,7 +31,7 @@ struct MonthlySummaryShareView: View {
|
||||
Text(String(format: String(localized: "share_summary_streak_badge"), streak))
|
||||
.font(.caption2.weight(.bold))
|
||||
}
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 5)
|
||||
.background(Color.white.opacity(0.18))
|
||||
@@ -41,17 +41,17 @@ struct MonthlySummaryShareView: View {
|
||||
|
||||
Text("Monthly Summary")
|
||||
.font(.title.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.top, 6)
|
||||
|
||||
// Hero value
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text("Portfolio Value")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
.foregroundStyle(.white.opacity(0.85))
|
||||
Text(totalValue)
|
||||
.font(.system(size: 40, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.minimumScaleFactor(0.6)
|
||||
.lineLimit(1)
|
||||
}
|
||||
@@ -66,9 +66,9 @@ struct MonthlySummaryShareView: View {
|
||||
Spacer()
|
||||
Text("since last check-in")
|
||||
.font(.caption)
|
||||
.foregroundColor(.white.opacity(0.75))
|
||||
.foregroundStyle(.white.opacity(0.75))
|
||||
}
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 10)
|
||||
.background(Color.white.opacity(0.15))
|
||||
@@ -85,14 +85,14 @@ struct MonthlySummaryShareView: View {
|
||||
Text(mood.title)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
}
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
if let rating {
|
||||
HStack(spacing: 2) {
|
||||
ForEach(1...5, id: \.self) { i in
|
||||
Image(systemName: i <= rating ? "star.fill" : "star")
|
||||
.font(.caption)
|
||||
.foregroundColor(.white.opacity(i <= rating ? 1 : 0.4))
|
||||
.foregroundStyle(.white.opacity(i <= rating ? 1 : 0.4))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,10 +114,10 @@ struct MonthlySummaryShareView: View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Tracked with")
|
||||
.font(.caption2.weight(.medium))
|
||||
.foregroundColor(.white.opacity(0.7))
|
||||
.foregroundStyle(.white.opacity(0.7))
|
||||
Text(appName)
|
||||
.font(.subheadline.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -91,7 +91,7 @@ struct QuickUpdateView: View {
|
||||
Image(systemName: "arrow.down.circle.fill")
|
||||
}
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.vertical, 2)
|
||||
}
|
||||
.listRowBackground(Color.appPrimary)
|
||||
@@ -243,14 +243,14 @@ struct QuickUpdateView: View {
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 1)
|
||||
.background(Color.appSecondary.opacity(0.15))
|
||||
.foregroundColor(.appSecondary)
|
||||
.foregroundStyle(Color.appSecondary)
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
if source.latestValue != .zero {
|
||||
Text(source.latestValue.currencyString)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
@@ -269,7 +269,7 @@ struct QuickUpdateView: View {
|
||||
HStack {
|
||||
Text(String(localized: "quick_update_contribution_label"))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
TextField(
|
||||
String(localized: "quick_update_contribution_placeholder"),
|
||||
@@ -279,7 +279,7 @@ struct QuickUpdateView: View {
|
||||
.multilineTextAlignment(.trailing)
|
||||
.frame(width: 120)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ private func eyebrow(_ text: String) -> some View {
|
||||
Text(text.uppercased())
|
||||
.font(.caption2.weight(.bold))
|
||||
.tracking(2.2)
|
||||
.foregroundColor(ShareCardStyle.green)
|
||||
.foregroundStyle(ShareCardStyle.green)
|
||||
}
|
||||
|
||||
private func deltaChip(_ text: String, positive: Bool, large: Bool = false) -> some View {
|
||||
@@ -167,7 +167,7 @@ private func deltaChip(_ text: String, positive: Bool, large: Bool = false) -> s
|
||||
Text(text)
|
||||
.font((large ? Font.headline : Font.caption).weight(.bold))
|
||||
}
|
||||
.foregroundColor(positive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42))
|
||||
.foregroundStyle(positive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42))
|
||||
.padding(.horizontal, large ? 12 : 9)
|
||||
.padding(.vertical, large ? 6 : 4)
|
||||
.background(
|
||||
@@ -179,11 +179,11 @@ private func metricRow(_ title: String, _ value: String, theme: ShareCardTheme)
|
||||
HStack {
|
||||
Text(title)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(theme.mutedInk)
|
||||
.foregroundStyle(theme.mutedInk)
|
||||
Spacer()
|
||||
Text(value)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(theme.ink)
|
||||
.foregroundStyle(theme.ink)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.minimumScaleFactor(0.7)
|
||||
.lineLimit(1)
|
||||
@@ -203,13 +203,13 @@ private func brandHeader(theme: ShareCardTheme, asOf: String?) -> some View {
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
Text("Portfolio Journal")
|
||||
.font(.system(.title2, design: .rounded).weight(.heavy))
|
||||
.foregroundColor(theme.ink)
|
||||
.foregroundStyle(theme.ink)
|
||||
.minimumScaleFactor(0.7)
|
||||
.lineLimit(1)
|
||||
if let asOf {
|
||||
Text(String(format: String(localized: "share_as_of"), asOf))
|
||||
.font(.caption2.weight(.medium))
|
||||
.foregroundColor(theme.mutedInk)
|
||||
.foregroundStyle(theme.mutedInk)
|
||||
}
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
@@ -223,11 +223,11 @@ private func downloadFooter(qrCodeImage: UIImage?, theme: ShareCardTheme) -> som
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
Text(String(localized: "share_cta_headline"))
|
||||
.font(.subheadline.weight(.bold))
|
||||
.foregroundColor(theme.ink)
|
||||
.foregroundStyle(theme.ink)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Text(String(localized: "share_cta_sub"))
|
||||
.font(.caption2.weight(.medium))
|
||||
.foregroundColor(theme.mutedInk)
|
||||
.foregroundStyle(theme.mutedInk)
|
||||
}
|
||||
|
||||
Spacer(minLength: 8)
|
||||
@@ -295,13 +295,13 @@ struct PortfolioValueShareCardView: View {
|
||||
eyebrow(String(localized: "share_hero_eyebrow"))
|
||||
Text(heroPercent ?? totalValue)
|
||||
.font(.system(size: storyFormat ? 52 : 44, weight: .heavy, design: .rounded))
|
||||
.foregroundColor(heroIsPercent ? (isPositive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42)) : theme.ink)
|
||||
.foregroundStyle(heroIsPercent ? (isPositive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42)) : theme.ink)
|
||||
.minimumScaleFactor(0.5)
|
||||
.lineLimit(1)
|
||||
if heroIsPercent && !totalValue.isEmpty {
|
||||
Text(totalValue)
|
||||
.font(.title3.weight(.bold))
|
||||
.foregroundColor(theme.ink)
|
||||
.foregroundStyle(theme.ink)
|
||||
.minimumScaleFactor(0.5)
|
||||
.lineLimit(1)
|
||||
}
|
||||
@@ -363,7 +363,7 @@ struct MonthlyCheckInShareCardView: View {
|
||||
eyebrow(summary.formattedMonthYear)
|
||||
Text("Monthly Check-in")
|
||||
.font(.title.weight(.bold))
|
||||
.foregroundColor(theme.ink)
|
||||
.foregroundStyle(theme.ink)
|
||||
.padding(.top, 4)
|
||||
|
||||
VStack(spacing: 9) {
|
||||
@@ -472,8 +472,8 @@ struct PortfolioShareOptionsView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 14)
|
||||
.background(Color.appPrimary)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(14)
|
||||
.foregroundStyle(.white)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 14))
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
@@ -41,7 +41,7 @@ struct GoalEditorView: View {
|
||||
TextField("Goal name", text: $name)
|
||||
HStack {
|
||||
Text(currencySymbol)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
TextField("Target amount", text: $targetAmount)
|
||||
.keyboardType(.decimalPad)
|
||||
}
|
||||
@@ -73,10 +73,10 @@ struct GoalEditorView: View {
|
||||
}
|
||||
.navigationTitle(goal == nil ? "New Goal" : "Edit Goal")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Save") { saveGoal() }
|
||||
.disabled(!isValid)
|
||||
}
|
||||
@@ -86,7 +86,7 @@ struct GoalEditorView: View {
|
||||
.presentationDragIndicator(.visible)
|
||||
.onAppear {
|
||||
guard let goal, !didLoadGoal else { return }
|
||||
name = goal.name ?? ""
|
||||
name = goal.name
|
||||
if let amount = goal.targetAmount?.decimalValue {
|
||||
targetAmount = formatDecimalForInput(amount)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ struct GoalProgressBar: View {
|
||||
.overlay(alignment: .leading) {
|
||||
Image(systemName: iconName)
|
||||
.font(.caption2)
|
||||
.foregroundColor(iconColor)
|
||||
.foregroundStyle(iconColor)
|
||||
.offset(x: iconOffset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,10 @@ struct GoalShareCardView: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Goal Progress")
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
.foregroundStyle(.white.opacity(0.8))
|
||||
Text(name)
|
||||
.font(.title2.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
|
||||
GoalProgressBar(
|
||||
@@ -51,27 +51,27 @@ struct GoalShareCardView: View {
|
||||
|
||||
Text("\(progressText) complete")
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
.foregroundStyle(.white.opacity(0.85))
|
||||
|
||||
if privacyMode {
|
||||
HStack {
|
||||
Text("Progress")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.white.opacity(0.9))
|
||||
.foregroundStyle(.white.opacity(0.9))
|
||||
Spacer()
|
||||
Text(progressText)
|
||||
.font(.headline.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
} else {
|
||||
HStack {
|
||||
Text(displayCurrentValue)
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
Spacer()
|
||||
Text("of \(targetValue.currencyString)")
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
.foregroundStyle(.white.opacity(0.8))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ struct GoalShareCardView: View {
|
||||
Text("Target: \(targetDate.mediumDateString)")
|
||||
.font(.caption.weight(.medium))
|
||||
}
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
.foregroundStyle(.white.opacity(0.85))
|
||||
}
|
||||
|
||||
if let estimatedCompletionDate {
|
||||
@@ -92,7 +92,7 @@ struct GoalShareCardView: View {
|
||||
Text("Est. completion: \(estimatedCompletionDate.mediumDateString)")
|
||||
.font(.caption.weight(.medium))
|
||||
}
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
.foregroundStyle(.white.opacity(0.85))
|
||||
}
|
||||
|
||||
if privacyMode {
|
||||
@@ -101,7 +101,7 @@ struct GoalShareCardView: View {
|
||||
Text("Privacy mode enabled")
|
||||
}
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.white.opacity(0.7))
|
||||
.foregroundStyle(.white.opacity(0.7))
|
||||
}
|
||||
|
||||
Spacer(minLength: 0)
|
||||
@@ -124,10 +124,10 @@ struct GoalShareCardView: View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Powered by")
|
||||
.font(.caption2.weight(.medium))
|
||||
.foregroundColor(.white.opacity(0.7))
|
||||
.foregroundStyle(.white.opacity(0.7))
|
||||
Text("Portfolio Journal")
|
||||
.font(.subheadline.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -144,7 +144,7 @@ struct GoalShareCardView: View {
|
||||
|
||||
Text("Scan to download")
|
||||
.font(.system(size: 7, weight: .medium))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
.foregroundStyle(.white.opacity(0.8))
|
||||
}
|
||||
} else {
|
||||
// Fallback if QR code generation fails
|
||||
@@ -154,7 +154,7 @@ struct GoalShareCardView: View {
|
||||
Text("App Store")
|
||||
.font(.caption2.weight(.semibold))
|
||||
}
|
||||
.foregroundColor(.white.opacity(0.9))
|
||||
.foregroundStyle(.white.opacity(0.9))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ struct GoalsView: View {
|
||||
}
|
||||
.navigationTitle("Goals")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Picker(String(localized: "goals_filter_active"), selection: $goalFilter) {
|
||||
ForEach(GoalFilter.allCases) { filter in
|
||||
Text(filter.label).tag(filter)
|
||||
@@ -80,7 +80,7 @@ struct GoalsView: View {
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button {
|
||||
showingAddGoal = true
|
||||
} label: {
|
||||
@@ -145,19 +145,19 @@ struct GoalsView: View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "target")
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Set your first goal")
|
||||
.font(.headline)
|
||||
Text("Track progress toward milestones like \(AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currencySymbol)1M and share your wins.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
Button {
|
||||
showingAddGoal = true
|
||||
} label: {
|
||||
Text(String(localized: "goals_empty_add_cta"))
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 10)
|
||||
.background(Color.appPrimary)
|
||||
@@ -170,13 +170,13 @@ struct GoalsView: View {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "archivebox")
|
||||
.font(.system(size: 40))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(String(localized: "goals_all_active_achieved"))
|
||||
.font(.headline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Switch to \"Archived\" or \"All\" to see other goals.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -186,10 +186,10 @@ struct GoalsView: View {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "archivebox")
|
||||
.font(.system(size: 40))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(String(localized: "goals_empty_archived"))
|
||||
.font(.headline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 32)
|
||||
@@ -197,12 +197,12 @@ struct GoalsView: View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "target")
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Set your first goal")
|
||||
.font(.headline)
|
||||
Text("Track progress toward milestones like \(AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currencySymbol)1M and share your wins.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -265,7 +265,7 @@ struct GoalRowView: View {
|
||||
if isAchieved {
|
||||
Text("Achieved")
|
||||
.font(.caption2.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 3)
|
||||
.background(Color.appSuccess)
|
||||
@@ -280,7 +280,7 @@ struct GoalRowView: View {
|
||||
Text(category.name)
|
||||
.font(.caption2.weight(.semibold))
|
||||
}
|
||||
.foregroundColor(category.color)
|
||||
.foregroundStyle(category.color)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 3)
|
||||
.background(category.color.opacity(0.15))
|
||||
@@ -293,21 +293,21 @@ struct GoalRowView: View {
|
||||
.hiddenBalance()
|
||||
Text("of \(goal.targetDecimal.currencyString)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.hiddenBalance()
|
||||
}
|
||||
|
||||
if let targetDate = goal.targetDate {
|
||||
Text("Target date: \(targetDate.mediumDateString)")
|
||||
.font(.caption)
|
||||
.foregroundColor(targetDateColor)
|
||||
.foregroundStyle(targetDateColor)
|
||||
}
|
||||
|
||||
if let paceStatus {
|
||||
Text(paceStatus.statusText)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(
|
||||
isAchieved ? .appSuccess : (paceStatus.isBehind ? .appWarning : .positiveGreen)
|
||||
.foregroundStyle(
|
||||
isAchieved ? Color.appSuccess : (paceStatus.isBehind ? Color.appWarning : Color.positiveGreen)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ struct GoalRowView: View {
|
||||
showingShareOptions = true
|
||||
} label: {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
.padding(.top, 2)
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
@@ -366,13 +366,13 @@ struct GoalRowView: View {
|
||||
case .unreachable:
|
||||
Label(String(localized: "goal_projection_add_contributions"), systemImage: "plus.circle")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
default:
|
||||
HStack(spacing: 8) {
|
||||
if let date = projection.projectedDate {
|
||||
Text(String(format: String(localized: "goal_projection_projected"), date.monthYearString))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
onTrackChip
|
||||
Spacer(minLength: 0)
|
||||
@@ -409,7 +409,7 @@ struct GoalRowView: View {
|
||||
if projection.status != .unreachable {
|
||||
Text(chipText)
|
||||
.font(.caption2.weight(.semibold))
|
||||
.foregroundColor(chipColor)
|
||||
.foregroundStyle(chipColor)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 3)
|
||||
.background(chipColor.opacity(0.15))
|
||||
|
||||
@@ -33,7 +33,7 @@ struct OnboardingICloudCheckView: View {
|
||||
.frame(width: 100, height: 100)
|
||||
Image(systemName: "icloud.fill")
|
||||
.font(.system(size: 50))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
|
||||
VStack(spacing: 14) {
|
||||
@@ -43,7 +43,7 @@ struct OnboardingICloudCheckView: View {
|
||||
|
||||
Text(String(localized: "icloud_check_description"))
|
||||
.font(.body)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
}
|
||||
@@ -59,11 +59,11 @@ struct OnboardingICloudCheckView: View {
|
||||
} label: {
|
||||
Label("Restore from iCloud", systemImage: "icloud.and.arrow.down")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.appPrimary)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -71,7 +71,7 @@ struct OnboardingICloudCheckView: View {
|
||||
} label: {
|
||||
Text("Start Fresh")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 24)
|
||||
@@ -96,7 +96,7 @@ struct OnboardingICloudCheckView: View {
|
||||
.frame(width: 100, height: 100)
|
||||
Image(systemName: "checkmark.icloud.fill")
|
||||
.font(.system(size: 50))
|
||||
.foregroundColor(.positiveGreen)
|
||||
.foregroundStyle(Color.positiveGreen)
|
||||
}
|
||||
|
||||
VStack(spacing: 14) {
|
||||
@@ -106,7 +106,7 @@ struct OnboardingICloudCheckView: View {
|
||||
|
||||
Text(String(localized: "icloud_enabled_description"))
|
||||
.font(.body)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
}
|
||||
@@ -123,11 +123,11 @@ struct OnboardingICloudCheckView: View {
|
||||
} label: {
|
||||
Text("Got it")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.positiveGreen)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
.padding(.horizontal, 24)
|
||||
.padding(.bottom, 40)
|
||||
|
||||
@@ -88,11 +88,11 @@ struct OnboardingView: View {
|
||||
} label: {
|
||||
Text("Continue")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.appPrimary)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -101,7 +101,7 @@ struct OnboardingView: View {
|
||||
} label: {
|
||||
Text("Skip")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
@@ -109,11 +109,11 @@ struct OnboardingView: View {
|
||||
} label: {
|
||||
Text("Get Started")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.appPrimary)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ struct OnboardingView: View {
|
||||
categoryRepository.createDefaultCategoriesIfNeeded()
|
||||
|
||||
// Create default account if needed
|
||||
AccountRepository().createDefaultAccountIfNeeded()
|
||||
_ = AccountRepository().createDefaultAccountIfNeeded()
|
||||
|
||||
// Mark onboarding as complete
|
||||
let context = CoreDataStack.shared.viewContext
|
||||
@@ -201,7 +201,7 @@ struct OnboardingPageView: View {
|
||||
|
||||
Image(systemName: page.icon)
|
||||
.font(.system(size: 50))
|
||||
.foregroundColor(page.color)
|
||||
.foregroundStyle(page.color)
|
||||
}
|
||||
|
||||
// Content
|
||||
@@ -212,7 +212,7 @@ struct OnboardingPageView: View {
|
||||
|
||||
Text(page.description)
|
||||
.font(.body)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
}
|
||||
@@ -255,7 +255,7 @@ struct OnboardingQuickStartView: View {
|
||||
|
||||
Text(String(localized: "onboarding_quickstart_subtitle"))
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 30)
|
||||
}
|
||||
@@ -268,8 +268,8 @@ struct OnboardingQuickStartView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.appPrimary)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.foregroundStyle(.white)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
|
||||
Button(action: onImport) {
|
||||
@@ -278,8 +278,8 @@ struct OnboardingQuickStartView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 12)
|
||||
.background(Color.appPrimary.opacity(0.1))
|
||||
.foregroundColor(.appPrimary)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,13 +297,13 @@ struct OnboardingQuickStartView: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color.gray.opacity(0.1))
|
||||
.cornerRadius(12)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
|
||||
|
||||
Toggle("Sync with iCloud (optional)", isOn: $cloudSyncEnabled)
|
||||
.padding()
|
||||
.background(Color.gray.opacity(0.1))
|
||||
.cornerRadius(12)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
|
||||
Spacer().frame(height: 8)
|
||||
@@ -326,7 +326,7 @@ struct WelcomeView: View {
|
||||
|
||||
Image(systemName: "hand.wave.fill")
|
||||
.font(.system(size: 60))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
if let name = userName {
|
||||
@@ -341,7 +341,7 @@ struct WelcomeView: View {
|
||||
? "Your investment data has been synced from iCloud."
|
||||
: "Your investment data stays on this device.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
|
||||
@@ -354,11 +354,11 @@ struct WelcomeView: View {
|
||||
} label: {
|
||||
Text("Continue")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.foregroundStyle(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.appPrimary)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
.padding(.horizontal, 24)
|
||||
.padding(.bottom, 40)
|
||||
|
||||
@@ -16,11 +16,11 @@ private struct PremiumChartPreview: View {
|
||||
HStack(alignment: .firstTextBaseline) {
|
||||
Text("€24,750")
|
||||
.font(.system(size: 18, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.primary)
|
||||
.foregroundStyle(.primary)
|
||||
Spacer()
|
||||
Text("+34.2%")
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.positiveGreen)
|
||||
.foregroundStyle(Color.positiveGreen)
|
||||
.padding(.horizontal, 7)
|
||||
.padding(.vertical, 3)
|
||||
.background(Color.positiveGreen.opacity(0.12))
|
||||
@@ -141,7 +141,7 @@ struct PaywallView: View {
|
||||
} label: {
|
||||
Image(systemName: "xmark")
|
||||
.font(.system(size: 14, weight: .semibold))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(10)
|
||||
.background(Color(.secondarySystemBackground))
|
||||
.clipShape(Circle())
|
||||
@@ -174,7 +174,7 @@ struct PaywallView: View {
|
||||
|
||||
Text("One payment. Every feature. Forever.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
@@ -189,7 +189,7 @@ struct PaywallView: View {
|
||||
}
|
||||
.padding(20)
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -199,11 +199,11 @@ struct PaywallView: View {
|
||||
HStack(spacing: 4) {
|
||||
Text(iapService.formattedPrice)
|
||||
.font(.system(size: 28, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
|
||||
Text("· one-time · Family Sharing")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,24 +242,24 @@ struct PaywallView: View {
|
||||
HStack(spacing: 6) {
|
||||
Text(title)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.primary)
|
||||
.foregroundStyle(.primary)
|
||||
if let badge, !badge.isEmpty {
|
||||
Text(badge)
|
||||
.font(.caption2.weight(.bold))
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 2)
|
||||
.background(Color.appSecondary.opacity(0.15))
|
||||
.foregroundColor(.appSecondary)
|
||||
.foregroundStyle(Color.appSecondary)
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
Text(price)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
Image(systemName: selectedPlan == plan ? "checkmark.circle.fill" : "circle")
|
||||
.foregroundColor(selectedPlan == plan ? .appPrimary : .secondary)
|
||||
.foregroundStyle(selectedPlan == plan ? Color.appPrimary : .secondary)
|
||||
}
|
||||
.padding(12)
|
||||
.background(Color(.systemBackground))
|
||||
@@ -268,7 +268,7 @@ struct PaywallView: View {
|
||||
.stroke(selectedPlan == plan ? Color.appPrimary : Color.gray.opacity(0.25),
|
||||
lineWidth: selectedPlan == plan ? 2 : 1)
|
||||
)
|
||||
.cornerRadius(AppConstants.UI.smallCornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
@@ -291,8 +291,8 @@ struct PaywallView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.appPrimary)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.foregroundStyle(.white)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
.disabled(isPurchasing)
|
||||
}
|
||||
@@ -305,7 +305,7 @@ struct PaywallView: View {
|
||||
} label: {
|
||||
Text("Restore Purchases")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
.disabled(isPurchasing)
|
||||
}
|
||||
@@ -316,17 +316,17 @@ struct PaywallView: View {
|
||||
VStack(spacing: 4) {
|
||||
Text("Payment charged to your Apple ID account.")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
HStack(spacing: 12) {
|
||||
Link("Terms", destination: URL(string: AppConstants.URLs.termsOfService)!)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Link("Privacy", destination: URL(string: AppConstants.URLs.privacyPolicy)!)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,7 +381,7 @@ struct BenefitRow: View {
|
||||
HStack(spacing: 14) {
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 20))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
.frame(width: 28)
|
||||
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
@@ -390,13 +390,13 @@ struct BenefitRow: View {
|
||||
|
||||
Text(subtitle)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.positiveGreen)
|
||||
.foregroundStyle(Color.positiveGreen)
|
||||
.font(.system(size: 18))
|
||||
}
|
||||
.padding(.vertical, 2)
|
||||
@@ -439,7 +439,7 @@ struct CompactPaywallBanner: View {
|
||||
|
||||
Text("Unlimited sources, advanced charts & more")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -452,13 +452,13 @@ struct CompactPaywallBanner: View {
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(Color.appPrimary)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(16)
|
||||
.foregroundStyle(.white)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
}
|
||||
@@ -493,8 +493,8 @@ struct PremiumLockOverlay: View {
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 10)
|
||||
.background(Color.appPrimary)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(20)
|
||||
.foregroundStyle(.white)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
|
||||
@@ -11,13 +11,13 @@ struct AllocationTargetsView: View {
|
||||
Text("Total Targets")
|
||||
Spacer()
|
||||
Text(String(format: "%.0f%%", totalTargets))
|
||||
.foregroundColor(totalTargets == 100 ? .positiveGreen : .secondary)
|
||||
.foregroundStyle(totalTargets == 100 ? Color.positiveGreen : .secondary)
|
||||
}
|
||||
|
||||
if totalTargets != 100 {
|
||||
Text("Targets don't need to be perfect, but aiming for 100% keeps the drift view accurate.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ struct AllocationTargetsView: View {
|
||||
.frame(width: 60)
|
||||
|
||||
Text("%")
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
|
||||
@@ -29,7 +29,7 @@ struct CSVMappingView: View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(pair.0)
|
||||
.font(.caption2.weight(.semibold))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(pair.1.isEmpty ? "—" : pair.1)
|
||||
.font(.caption)
|
||||
.lineLimit(2)
|
||||
@@ -117,10 +117,10 @@ struct CSVMappingView: View {
|
||||
.navigationTitle("Map Columns")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Import") {
|
||||
onComplete(config)
|
||||
dismiss()
|
||||
@@ -261,10 +261,10 @@ private struct MappingRow: View {
|
||||
HStack(spacing: 4) {
|
||||
Text(selectionLabel)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(index == ImportService.CSVMappingConfig.notMapped ? .secondary : .appPrimary)
|
||||
.foregroundStyle(index == ImportService.CSVMappingConfig.notMapped ? .secondary : Color.appPrimary)
|
||||
Image(systemName: "chevron.up.chevron.down")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -332,10 +332,10 @@ private struct MappingRowWithConstant: View {
|
||||
HStack(spacing: 4) {
|
||||
Text(selectionLabel)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(index == ImportService.CSVMappingConfig.notMapped ? .secondary : .appPrimary)
|
||||
.foregroundStyle(index == ImportService.CSVMappingConfig.notMapped ? .secondary : Color.appPrimary)
|
||||
Image(systemName: "chevron.up.chevron.down")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ struct CategoriesView: View {
|
||||
List {
|
||||
if categoryRepository.categories.isEmpty {
|
||||
Text(String(localized: "categories_empty"))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.vertical, 32)
|
||||
} else {
|
||||
@@ -54,14 +54,14 @@ struct CategoriesView: View {
|
||||
.navigationTitle("Categories")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button {
|
||||
showingAddCategory = true
|
||||
} label: {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
EditButton()
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ private struct CategoryRow: View {
|
||||
.frame(width: 40, height: 40)
|
||||
Image(systemName: category.icon)
|
||||
.font(.system(size: 16))
|
||||
.foregroundColor(category.color)
|
||||
.foregroundStyle(category.color)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
@@ -107,7 +107,7 @@ private struct CategoryRow: View {
|
||||
.font(.body)
|
||||
Text(category.sourceCount == 1 ? "1 source" : "\(category.sourceCount) sources")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -186,20 +186,20 @@ struct CategoryEditorView: View {
|
||||
.frame(width: 44, height: 44)
|
||||
Image(systemName: selectedIcon)
|
||||
.font(.system(size: 18))
|
||||
.foregroundColor(Color(hex: selectedColor) ?? .blue)
|
||||
.foregroundStyle(Color(hex: selectedColor) ?? .blue)
|
||||
}
|
||||
Text(name.isEmpty ? String(localized: "category_name_placeholder") : name)
|
||||
.foregroundColor(name.isEmpty ? .secondary : .primary)
|
||||
.foregroundStyle(name.isEmpty ? .secondary : .primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(category == nil ? "Add Category" : "Edit Category")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Save") { save() }
|
||||
.fontWeight(.semibold)
|
||||
.disabled(name.trimmingCharacters(in: .whitespaces).isEmpty)
|
||||
@@ -259,7 +259,7 @@ private struct IconSwatch: View {
|
||||
.frame(width: 44, height: 44)
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 18))
|
||||
.foregroundColor(isSelected ? color : .secondary)
|
||||
.foregroundStyle(isSelected ? color : .secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import UniformTypeIdentifiers
|
||||
import UIKit
|
||||
|
||||
@@ -88,7 +89,7 @@ struct ImportDataView: View {
|
||||
ProgressView()
|
||||
Text("Reviewing import…")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.top, 8)
|
||||
}
|
||||
@@ -98,7 +99,7 @@ struct ImportDataView: View {
|
||||
ProgressView(value: importProgress)
|
||||
Text(importStatus)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.top, 8)
|
||||
}
|
||||
@@ -133,7 +134,7 @@ struct ImportDataView: View {
|
||||
.navigationTitle("Import Data")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Done") { dismiss() }
|
||||
.disabled(isImporting)
|
||||
}
|
||||
@@ -244,7 +245,7 @@ Personal,Stocks,Index Fund,2024-01-01,15000,12000,Long-term
|
||||
.font(.caption.monospaced())
|
||||
Text("Account, Contribution, and Notes are optional. Dates accept / or - and 24h/12h time.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +326,7 @@ Personal,Stocks,Index Fund,2024-01-01,15000,12000,Long-term
|
||||
Text("Account")
|
||||
Spacer()
|
||||
Text(selectedAccountName ?? "Personal")
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -333,7 +334,7 @@ Personal,Stocks,Index Fund,2024-01-01,15000,12000,Long-term
|
||||
} footer: {
|
||||
if let accountErrorMessage {
|
||||
Text(accountErrorMessage)
|
||||
.foregroundColor(.negativeRed)
|
||||
.foregroundStyle(Color.negativeRed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import StoreKit
|
||||
|
||||
struct AddSourceView: View {
|
||||
@@ -54,25 +55,25 @@ struct AddSourceView: View {
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Category")
|
||||
.foregroundColor(.primary)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
if let category = selectedCategory {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: category.icon)
|
||||
.foregroundColor(category.color)
|
||||
.foregroundStyle(category.color)
|
||||
Text(category.name)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
} else {
|
||||
Text("Select")
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -80,10 +81,10 @@ struct AddSourceView: View {
|
||||
} footer: {
|
||||
if let error = duplicateError {
|
||||
Text(error)
|
||||
.foregroundColor(.negativeRed)
|
||||
.foregroundStyle(Color.negativeRed)
|
||||
} else {
|
||||
Text(String(localized: "add_source_name_footer"))
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +93,7 @@ struct AddSourceView: View {
|
||||
Section {
|
||||
HStack {
|
||||
Text(currencySymbol)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
TextField("0.00", text: $initialValue)
|
||||
.keyboardType(.decimalPad)
|
||||
@@ -106,13 +107,13 @@ struct AddSourceView: View {
|
||||
.navigationTitle("Add Source")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Cancel") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Add") {
|
||||
saveSource()
|
||||
}
|
||||
@@ -262,17 +263,17 @@ struct CategoryPickerView: View {
|
||||
.frame(width: 40, height: 40)
|
||||
|
||||
Image(systemName: category.icon)
|
||||
.foregroundColor(category.color)
|
||||
.foregroundStyle(category.color)
|
||||
}
|
||||
|
||||
Text(category.name)
|
||||
.foregroundColor(.primary)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
if selectedCategory?.id == category.id {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,7 +281,7 @@ struct CategoryPickerView: View {
|
||||
.navigationTitle("Select Category")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Done") {
|
||||
dismiss()
|
||||
}
|
||||
@@ -334,22 +335,22 @@ struct EditSourceView: View {
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Category")
|
||||
.foregroundColor(.primary)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
if let category = selectedCategory {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: category.icon)
|
||||
.foregroundColor(category.color)
|
||||
.foregroundStyle(category.color)
|
||||
Text(category.name)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,13 +358,13 @@ struct EditSourceView: View {
|
||||
.navigationTitle("Edit Source")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Cancel") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Save") {
|
||||
saveChanges()
|
||||
}
|
||||
@@ -456,7 +457,7 @@ struct AddSnapshotView: View {
|
||||
|
||||
HStack {
|
||||
Text(viewModel.currencySymbol)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
TextField("Value", text: $viewModel.valueString)
|
||||
.keyboardType(.decimalPad)
|
||||
@@ -474,7 +475,7 @@ struct AddSnapshotView: View {
|
||||
if viewModel.previousValue != nil {
|
||||
Text(viewModel.previousValueString)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
} header: {
|
||||
Text("Snapshot Details")
|
||||
@@ -488,10 +489,10 @@ struct AddSnapshotView: View {
|
||||
Text("Change from previous")
|
||||
Spacer()
|
||||
Text("\(change) (\(percentage))")
|
||||
.foregroundColor(
|
||||
.foregroundStyle(
|
||||
(viewModel.changeFromPrevious ?? 0) >= 0
|
||||
? .positiveGreen
|
||||
: .negativeRed
|
||||
? Color.positiveGreen
|
||||
: Color.negativeRed
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -503,7 +504,7 @@ struct AddSnapshotView: View {
|
||||
if viewModel.includeContribution {
|
||||
HStack {
|
||||
Text(viewModel.currencySymbol)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
TextField("New capital added", text: $viewModel.contributionString)
|
||||
.keyboardType(.decimalPad)
|
||||
@@ -525,13 +526,13 @@ struct AddSnapshotView: View {
|
||||
.navigationTitle(viewModel.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Cancel") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button(viewModel.buttonTitle) {
|
||||
saveSnapshot()
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ struct SourceDetailView: View {
|
||||
.navigationTitle(viewModel.safeSourceName)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Menu {
|
||||
Button {
|
||||
viewModel.showingEditSource = true
|
||||
@@ -129,11 +129,11 @@ struct SourceDetailView: View {
|
||||
Text(viewModel.categoryName)
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundColor(Color(hex: viewModel.categoryColor) ?? .gray)
|
||||
.foregroundStyle(Color(hex: viewModel.categoryColor) ?? .gray)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background((Color(hex: viewModel.categoryColor) ?? .gray).opacity(0.1))
|
||||
.cornerRadius(20)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
|
||||
// Current value
|
||||
Text(viewModel.formattedCurrentValue)
|
||||
@@ -147,17 +147,17 @@ struct SourceDetailView: View {
|
||||
Text("(\(viewModel.formattedPercentageReturn))")
|
||||
}
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundColor(viewModel.isPositiveReturn ? .positiveGreen : .negativeRed)
|
||||
.foregroundStyle(viewModel.isPositiveReturn ? Color.positiveGreen : Color.negativeRed)
|
||||
|
||||
// Last updated
|
||||
Text("Last updated: \(viewModel.lastUpdated)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -172,8 +172,8 @@ struct SourceDetailView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.appPrimary)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.foregroundStyle(.white)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ struct SourceDetailView: View {
|
||||
HStack {
|
||||
Label(String(localized: "source_monthly_contribution_title"), systemImage: "arrow.down.circle.fill")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(.primary)
|
||||
.foregroundStyle(.primary)
|
||||
Spacer()
|
||||
Button {
|
||||
if editingContribution {
|
||||
@@ -204,7 +204,7 @@ struct SourceDetailView: View {
|
||||
} label: {
|
||||
Text(editingContribution ? String(localized: "done") : String(localized: "edit"))
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,20 +213,20 @@ struct SourceDetailView: View {
|
||||
.keyboardType(.decimalPad)
|
||||
.padding(8)
|
||||
.background(Color(.systemGray6))
|
||||
.cornerRadius(8)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
} else {
|
||||
Text(current.map { $0.currencyString } ?? String(localized: "source_monthly_contribution_not_set"))
|
||||
.font(.subheadline)
|
||||
.foregroundColor(current != nil ? .primary : .secondary)
|
||||
.foregroundStyle(current != nil ? .primary : .secondary)
|
||||
}
|
||||
|
||||
Text(String(localized: "source_monthly_contribution_hint"))
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
.confirmationDialog(
|
||||
String(localized: "source_monthly_contribution_apply_title"),
|
||||
@@ -316,7 +316,7 @@ struct SourceDetailView: View {
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.appSecondary.opacity(0.15))
|
||||
.cornerRadius(8)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,18 +347,18 @@ struct SourceDetailView: View {
|
||||
Text("Unlock predictions on the chart")
|
||||
}
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
.padding(.vertical, 6)
|
||||
.padding(.horizontal, 10)
|
||||
.background(Color.appPrimary.opacity(0.1))
|
||||
.cornerRadius(10)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ struct SourceDetailView: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
|
||||
@@ -399,13 +399,13 @@ struct SourceDetailView: View {
|
||||
|
||||
Text("\(viewModel.snapshotCount)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
if viewModel.isHistoryLimited {
|
||||
HStack {
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(.appWarning)
|
||||
.foregroundStyle(Color.appWarning)
|
||||
Text("\(viewModel.hiddenSnapshotCount) older snapshots hidden. Upgrade for full history.")
|
||||
.font(.caption)
|
||||
|
||||
@@ -418,7 +418,7 @@ struct SourceDetailView: View {
|
||||
}
|
||||
.padding(8)
|
||||
.background(Color.appWarning.opacity(0.1))
|
||||
.cornerRadius(AppConstants.UI.smallCornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius))
|
||||
}
|
||||
|
||||
// Use LazyVStack for better performance with many snapshots
|
||||
@@ -464,12 +464,12 @@ struct SourceDetailView: View {
|
||||
if viewModel.isHistoryLimited && viewModel.hiddenSnapshotCount > 0 {
|
||||
Text("+ \(viewModel.hiddenSnapshotCount) older snapshots hidden")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(AppConstants.UI.cornerRadius)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
|
||||
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
|
||||
}
|
||||
}
|
||||
@@ -484,7 +484,7 @@ struct MetricCard: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(title)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Text(value)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
@@ -508,7 +508,7 @@ struct SnapshotRowView: View {
|
||||
if let notes = snapshot.notes, !notes.isEmpty {
|
||||
Text(notes)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
@@ -523,7 +523,7 @@ struct SnapshotRowView: View {
|
||||
if snapshot.contribution != nil && snapshot.decimalContribution > 0 {
|
||||
Text("+ \(snapshot.decimalContribution.currencyString)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.appPrimary)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
.hiddenBalance()
|
||||
}
|
||||
}
|
||||
@@ -532,10 +532,10 @@ struct SnapshotRowView: View {
|
||||
Button(action: onEdit) {
|
||||
Image(systemName: "pencil")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(6)
|
||||
.background(Color.gray.opacity(0.1))
|
||||
.cornerRadius(8)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
@@ -552,7 +552,7 @@ struct MetricChip: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(title)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Text(value)
|
||||
.font(.caption.weight(.semibold))
|
||||
@@ -561,7 +561,7 @@ struct MetricChip: View {
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 12)
|
||||
.background(Color.gray.opacity(0.1))
|
||||
.cornerRadius(10)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user