a8a9ad64f3
- 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
146 lines
5.4 KiB
Swift
146 lines
5.4 KiB
Swift
import SwiftUI
|
|
|
|
/// A polished, branded card summarising the month — designed to be rendered to
|
|
/// an image and shared (screenshot-worthy, marketing-facing). Pulls only from
|
|
/// data the Dashboard already computes.
|
|
struct MonthlySummaryShareView: View {
|
|
let monthLabel: String
|
|
let totalValue: String
|
|
let changeAmount: String
|
|
let changePercentage: String
|
|
let isPositive: Bool
|
|
let streak: Int
|
|
let mood: MonthlyCheckInMood?
|
|
let rating: Int?
|
|
let appName: String
|
|
var qrCodeImage: UIImage? = nil
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 0) {
|
|
// Header
|
|
HStack {
|
|
Text(monthLabel)
|
|
.font(.caption.weight(.semibold))
|
|
.foregroundStyle(.white.opacity(0.85))
|
|
.textCase(.uppercase)
|
|
Spacer()
|
|
if streak >= 2 {
|
|
HStack(spacing: 4) {
|
|
Image(systemName: "flame.fill")
|
|
.font(.caption2)
|
|
Text(String(format: String(localized: "share_summary_streak_badge"), streak))
|
|
.font(.caption2.weight(.bold))
|
|
}
|
|
.foregroundStyle(.white)
|
|
.padding(.horizontal, 10)
|
|
.padding(.vertical, 5)
|
|
.background(Color.white.opacity(0.18))
|
|
.clipShape(Capsule())
|
|
}
|
|
}
|
|
|
|
Text("Monthly Summary")
|
|
.font(.title.weight(.bold))
|
|
.foregroundStyle(.white)
|
|
.padding(.top, 6)
|
|
|
|
// Hero value
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
Text("Portfolio Value")
|
|
.font(.subheadline.weight(.semibold))
|
|
.foregroundStyle(.white.opacity(0.85))
|
|
Text(totalValue)
|
|
.font(.system(size: 40, weight: .bold, design: .rounded))
|
|
.foregroundStyle(.white)
|
|
.minimumScaleFactor(0.6)
|
|
.lineLimit(1)
|
|
}
|
|
.padding(.top, 22)
|
|
|
|
// Change since last check-in
|
|
HStack(spacing: 8) {
|
|
Image(systemName: isPositive ? "arrow.up.right" : "arrow.down.right")
|
|
.font(.subheadline.weight(.bold))
|
|
Text("\(changeAmount) (\(changePercentage))")
|
|
.font(.headline.weight(.bold))
|
|
Spacer()
|
|
Text("since last check-in")
|
|
.font(.caption)
|
|
.foregroundStyle(.white.opacity(0.75))
|
|
}
|
|
.foregroundStyle(.white)
|
|
.padding(.horizontal, 14)
|
|
.padding(.vertical, 10)
|
|
.background(Color.white.opacity(0.15))
|
|
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
|
|
.padding(.top, 18)
|
|
|
|
// Mood / rating
|
|
if mood != nil || rating != nil {
|
|
HStack(spacing: 14) {
|
|
if let mood {
|
|
HStack(spacing: 6) {
|
|
Image(systemName: mood.iconName)
|
|
.font(.subheadline)
|
|
Text(mood.title)
|
|
.font(.subheadline.weight(.semibold))
|
|
}
|
|
.foregroundStyle(.white)
|
|
}
|
|
if let rating {
|
|
HStack(spacing: 2) {
|
|
ForEach(1...5, id: \.self) { i in
|
|
Image(systemName: i <= rating ? "star.fill" : "star")
|
|
.font(.caption)
|
|
.foregroundStyle(.white.opacity(i <= rating ? 1 : 0.4))
|
|
}
|
|
}
|
|
}
|
|
Spacer()
|
|
}
|
|
.padding(.top, 16)
|
|
}
|
|
|
|
Spacer(minLength: 0)
|
|
|
|
// Footer / branding
|
|
HStack(spacing: 12) {
|
|
Image("BrandMark")
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 40, height: 40)
|
|
.clipShape(RoundedRectangle(cornerRadius: 9, style: .continuous))
|
|
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text("Tracked with")
|
|
.font(.caption2.weight(.medium))
|
|
.foregroundStyle(.white.opacity(0.7))
|
|
Text(appName)
|
|
.font(.subheadline.weight(.bold))
|
|
.foregroundStyle(.white)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
if let qrCodeImage {
|
|
Image(uiImage: qrCodeImage)
|
|
.interpolation(.none)
|
|
.resizable()
|
|
.frame(width: 52, height: 52)
|
|
.background(Color.white)
|
|
.clipShape(RoundedRectangle(cornerRadius: 7, style: .continuous))
|
|
}
|
|
}
|
|
.padding(.top, 20)
|
|
}
|
|
.padding(24)
|
|
.frame(width: 340, height: 440)
|
|
.background(ShareCardBackground())
|
|
.clipShape(RoundedRectangle(cornerRadius: 28, style: .continuous))
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 28, style: .continuous)
|
|
.stroke(Color.white.opacity(0.12), lineWidth: 1)
|
|
)
|
|
}
|
|
}
|