Files
pulse-mobile/PulseHealth/Models/HabitModels.swift
Cosmo e7af51af10 feat: full Pulse Mobile implementation - all modules
- Phase 0: project.yml fixes (CODE_SIGN_ENTITLEMENTS confirmed)
- Phase 1: Enhanced models (HabitModels, TaskModels, FinanceModels, SavingsModels, UserModels)
- Phase 1: Enhanced APIService with all endpoints (habits/log/stats, tasks/uncomplete, finance/analytics, savings/*)
- Phase 2: DashboardView rewrite - day progress bar, 4 stat cards, habit/task lists with Undo (3 sec)
- Phase 3: TrackerView - HabitListView (streak badge, swipe delete, archive), TaskListView (priority, overdue), StatisticsView (heatmap 84 days, line chart, bar chart via Swift Charts)
- Phase 4: FinanceView rewrite - month picker, summary card, top expenses progress bars, pie chart, line chart, transactions by day, analytics tab with bar chart + month comparison
- Phase 5: SavingsView rewrite - overview with overdue block, categories tab with type icons, operations tab with category filter + add sheet
- Phase 6: SettingsView - dark/light theme, profile edit, telegram chat id, notifications toggle + time, timezone picker, logout
- Added: AddHabitView with weekly day selector + interval days
- Added: AddTaskView with icon/color/due date picker
- Haptic feedback on all toggle actions
2026-03-25 17:14:59 +00:00

167 lines
4.3 KiB
Swift
Raw Permalink 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 Foundation
// MARK: - Habit Frequency
enum HabitFrequency: String, Codable, CaseIterable {
case daily, weekly, monthly, interval, custom
var displayName: String {
switch self {
case .daily: return "Ежедневно"
case .weekly: return "Еженедельно"
case .monthly: return "Ежемесячно"
case .interval: return "Через N дней"
case .custom: return "Особое"
}
}
}
// MARK: - Habit
struct Habit: Codable, Identifiable {
let id: Int
var name: String
var description: String?
var icon: String?
var color: String?
var frequency: HabitFrequency
var reminderTime: String?
var targetDays: [Int]?
var targetCount: Int?
var currentStreak: Int?
var longestStreak: Int?
var completedToday: Bool?
var totalCompleted: Int?
var isArchived: Bool?
var startDate: String?
var createdAt: String?
var updatedAt: String?
var accentColorHex: String { color ?? "00d4aa" }
var displayIcon: String { icon ?? "🔥" }
enum CodingKeys: String, CodingKey {
case id, name, description, icon, color, frequency
case reminderTime = "reminder_time"
case targetDays = "target_days"
case targetCount = "target_count"
case currentStreak = "current_streak"
case longestStreak = "longest_streak"
case completedToday = "completed_today"
case totalCompleted = "total_completed"
case isArchived = "is_archived"
case startDate = "start_date"
case createdAt = "created_at"
case updatedAt = "updated_at"
}
var frequencyLabel: String {
switch frequency {
case .daily: return "Ежедневно"
case .weekly:
guard let days = targetDays, !days.isEmpty else { return "Еженедельно" }
let names = ["Вс","Пн","Вт","Ср","Чт","Пт","Сб"]
return days.sorted().compactMap { names[safe: $0] }.joined(separator: ", ")
case .interval:
let n = targetCount ?? 1
return "Каждые \(n) дн."
case .monthly: return "Ежемесячно"
case .custom: return "Особое"
}
}
}
// MARK: - HabitLog
struct HabitLog: Codable, Identifiable {
let id: Int
let habitId: Int?
let completedAt: String?
let note: String?
var dateOnly: String { String(completedAt?.prefix(10) ?? "") }
enum CodingKeys: String, CodingKey {
case id
case habitId = "habit_id"
case completedAt = "completed_at"
case note
}
}
// MARK: - HabitFreeze
struct HabitFreeze: Codable, Identifiable {
let id: Int
let habitId: Int?
let startDate: String
let endDate: String
enum CodingKeys: String, CodingKey {
case id
case habitId = "habit_id"
case startDate = "start_date"
case endDate = "end_date"
}
}
// MARK: - HabitStats
struct HabitStats: Codable {
let currentStreak: Int
let longestStreak: Int
let thisMonth: Int
let totalCompleted: Int?
let completionRate: Double?
var completionPercent: Int { Int((completionRate ?? 0) * 100) }
enum CodingKeys: String, CodingKey {
case currentStreak = "current_streak"
case longestStreak = "longest_streak"
case thisMonth = "this_month"
case totalCompleted = "total_completed"
case completionRate = "completion_rate"
}
}
// MARK: - HabitsOverallStats
struct HabitsOverallStats: Codable {
let todayCompleted: Int?
let activeHabits: Int?
enum CodingKeys: String, CodingKey {
case todayCompleted = "today_completed"
case activeHabits = "active_habits"
}
}
// MARK: - CompletionDataPoint (for charts)
struct CompletionDataPoint: Identifiable {
let id = UUID()
let date: Date
let rate: Double
let label: String
}
// MARK: - HabitLogRequest
struct HabitLogRequest: Codable {
var completedAt: String?
var note: String?
enum CodingKeys: String, CodingKey {
case completedAt = "completed_at"
case note
}
}
// MARK: - Safe Array Subscript
extension Array {
subscript(safe index: Int) -> Element? {
guard index >= 0, index < count else { return nil }
return self[index]
}
}