Files
Balance/Balance/BalanceWatch/WatchRootView.swift
T
2026-07-14 16:20:39 +07:00

159 lines
6.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftData
import SwiftUI
struct WatchRootView: View {
@Environment(\.modelContext) private var modelContext
@Query(sort: \FinanceTransaction.date, order: .reverse)
private var transactions: [FinanceTransaction]
@AppStorage("currencyCode") private var currencyCode = SupportedCurrency.RUB.rawValue
@State private var showingAddTransaction = false
private var monthSummary: FinancePeriodSummary {
FinanceCalculations.summary(
transactions: transactions,
in: FinanceCalculations.monthInterval(containing: .now)
)
}
var body: some View {
NavigationStack {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
VStack(alignment: .leading, spacing: 3) {
Text("Общий баланс")
.font(.caption)
.foregroundStyle(.secondary)
Text(FinanceCalculations.totalBalance(transactions: transactions)
.formattedCurrency(code: currencyCode))
.font(.title3.bold())
.minimumScaleFactor(0.7)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(12)
.background(
LinearGradient(
colors: [.indigo, .purple],
startPoint: .topLeading,
endPoint: .bottomTrailing
),
in: RoundedRectangle(cornerRadius: 16)
)
.foregroundStyle(.white)
HStack(spacing: 8) {
watchMetric(
title: "Доходы",
amount: monthSummary.income,
tint: .green
)
watchMetric(
title: "Расходы",
amount: monthSummary.expenses,
tint: .red
)
}
Button {
showingAddTransaction = true
} label: {
Label("Добавить операцию", systemImage: "plus.circle.fill")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
VStack(spacing: 4) {
NavigationLink {
WatchAnalyticsView()
} label: {
Label("Аналитика", systemImage: "chart.xyaxis.line")
}
NavigationLink {
WatchTransactionsView()
} label: {
Label("Все операции", systemImage: "list.bullet.rectangle")
}
NavigationLink {
WatchBudgetsView()
} label: {
Label("Бюджеты", systemImage: "target")
}
NavigationLink {
WatchCategoriesView()
} label: {
Label("Категории", systemImage: "square.grid.2x2")
}
NavigationLink {
WatchBalanceAdjustmentView()
} label: {
Label("Корректировка", systemImage: "slider.horizontal.3")
}
NavigationLink {
WatchSettingsView()
} label: {
Label("Настройки", systemImage: "gearshape")
}
}
if !transactions.isEmpty {
Text("Последние")
.font(.headline)
ForEach(transactions.prefix(4)) { transaction in
HStack(spacing: 8) {
CategoryIconView(
systemName: transaction.categoryIcon,
emoji: transaction.categoryEmoji,
tint: transactionTint(transaction),
size: transaction.categoryEmoji.isEmpty ? 13 : 17,
containerSize: 30
)
VStack(alignment: .leading, spacing: 1) {
Text(transaction.categoryName)
.font(.caption)
.lineLimit(1)
Text((transaction.kind == .income ? "+" : "−")
+ transaction.amount.formattedCurrency(code: currencyCode))
.font(.caption2.weight(.semibold))
.foregroundStyle(transaction.kind == .income ? .green : .secondary)
}
Spacer()
}
}
}
}
}
.navigationTitle("Баланс")
.sheet(isPresented: $showingAddTransaction) {
WatchAddTransactionView()
}
}
.task {
while !Task.isCancelled {
await ServerAccountStore.shared.sync(context: modelContext)
try? await Task.sleep(nanoseconds: 120_000_000_000)
}
}
}
private func watchMetric(title: String, amount: Double, tint: Color) -> some View {
VStack(alignment: .leading, spacing: 3) {
Text(title)
.font(.caption2)
.foregroundStyle(.secondary)
Text(amount.formattedCurrency(code: currencyCode))
.font(.caption.bold())
.lineLimit(1)
.minimumScaleFactor(0.65)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(9)
.background(tint.opacity(0.12), in: RoundedRectangle(cornerRadius: 12))
}
private func transactionTint(_ transaction: FinanceTransaction) -> Color {
if transaction.categoryColorName == "auto" {
return FinanceCategory.find(named: transaction.categoryName)?.tint ?? .indigo
}
return FinanceCategory.color(named: transaction.categoryColorName)
}
}