feat: major app overhaul — API fixes, glassmorphism UI, health dashboard, notifications
API Integration: - Fix logHabit: send "date" instead of "completed_at" - Fix FinanceCategory: "icon" → "emoji" to match API - Fix task priorities: remove level 4, keep 1-3 matching API - Fix habit frequencies: map monthly/interval → "custom" for API - Add token refresh (401 → auto retry with new token) - Add proper error handling (remove try? in save functions, show errors in UI) - Add date field to savings transactions - Add MonthlyPaymentDetail and OverduePayment models - Fix habit completedToday: compute on client from logs (API doesn't return it) - Filter habits by day of week on client (daily/weekly/monthly/interval) Design System (glassmorphism): - New DesignSystem.swift: Theme colors, GlassCard modifier, GlowIcon, GlowStatCard - Custom tab bar with per-tab glow colors (VStack layout, not ZStack overlay) - Deep dark background #06060f across all views - Glass cards with gradient fill + stroke throughout app - App icon: glassmorphism style with teal glow Health Dashboard: - Compact ReadinessBanner with recommendation text - 8 metric tiles: sleep, HR, HRV, steps, SpO2, respiratory rate, energy, distance - Each tile with status indicator (good/ok/bad) and hint text - Heart rate card (min/avg/max) - Weekly trends card (averages) - Recovery score (weighted: 40% sleep, 35% HRV, 25% RHR) - Tips card with actionable recommendations - Sleep detail view with hypnogram (step chart of phases) - Sleep segments timeline from HealthKit (deep/rem/core/awake with exact times) - Line chart replacing bar chart for weekly data - Collect respiratory_rate and sleep phases with timestamps from HealthKit - Background sync every ~30min via BGProcessingTask Notifications: - NotificationService for local push notifications - Morning/evening reminders with native DatePicker (wheel) - Payment reminders: 5 days, 1 day, and day-of for recurring savings - Notification settings in Settings tab UI Fixes: - Fix color picker overflow: HStack → LazyVGrid 5 columns - Fix sheet headers: shorter text, proper padding - Fix task/habit toggle: separate tap zones (checkbox vs edit) - Fix deprecated onChange syntax for iOS 17+ - Savings overview: real monthly payments and detailed overdues from API - Settings: timezone as Menu picker, removed Telegram/server notifications sections - All sheets use .presentationDetents([.large]) Config: - project.yml: real DEVELOPMENT_TEAM, HealthKit + BackgroundModes capabilities - Info.plist: BGTaskScheduler + UIBackgroundModes - Assets.xcassets with AppIcon - CLAUDE.md project documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
146
CLAUDE.md
Normal file
146
CLAUDE.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Pulse Health — iOS App
|
||||
|
||||
## Overview
|
||||
iOS-приложение для управления жизнью: привычки, задачи, финансы, накопления, здоровье. Интегрируется с Apple Watch через HealthKit и серверным API.
|
||||
|
||||
- **Bundle ID:** com.daniil.pulsehealth
|
||||
- **Platform:** iOS 17+, SwiftUI, Swift 5.9
|
||||
- **Build:** XcodeGen (project.yml → .xcodeproj)
|
||||
- **Team ID:** V9AG8JTFLC
|
||||
- **Язык интерфейса:** Русский
|
||||
|
||||
## Architecture
|
||||
|
||||
### Project Structure
|
||||
```
|
||||
PulseHealth/
|
||||
├── App.swift # Entry point, AuthManager, BGTask registration
|
||||
├── Models/
|
||||
│ ├── AuthModels.swift # Login/Register/Refresh requests & responses
|
||||
│ ├── UserModels.swift # UserProfile, UpdateProfileRequest
|
||||
│ ├── HealthModels.swift # Readiness, Latest health, Heatmap, SleepSegment
|
||||
│ ├── TaskModels.swift # PulseTask, CreateTask, UpdateTask (priorities 1-3)
|
||||
│ ├── HabitModels.swift # Habit, HabitLog, HabitFreeze, HabitStats, HabitFrequency enum
|
||||
│ ├── FinanceModels.swift # Transaction, Category (emoji field, not icon), Summary, Analytics
|
||||
│ └── SavingsModels.swift # Category, Transaction, Stats, RecurringPlan, MonthlyPaymentDetail, OverduePayment
|
||||
├── Services/
|
||||
│ ├── APIService.swift # Main REST client (api.digital-home.site), auto token refresh on 401
|
||||
│ ├── HealthAPIService.swift # Health API (health.digital-home.site), separate JWT auth
|
||||
│ ├── HealthKitService.swift # HealthKit data collection & sync, sleep segments timeline
|
||||
│ └── NotificationService.swift # Local push notifications (morning/evening, task deadlines)
|
||||
├── Views/
|
||||
│ ├── DesignSystem.swift # Theme colors, GlassCard modifier, GlowIcon, GlowStatCard
|
||||
│ ├── MainTabView.swift # Custom tab bar with glow effects (VStack layout, not ZStack)
|
||||
│ ├── LoginView.swift # Auth + ForgotPassword
|
||||
│ ├── Dashboard/
|
||||
│ │ └── DashboardView.swift # Home: progress, stats, habits, tasks, FAB
|
||||
│ ├── Health/
|
||||
│ │ ├── HealthView.swift # Full health dashboard: readiness, metrics, HR, trends, recovery, tips
|
||||
│ │ ├── MetricCardView.swift # SleepCard, StepsCard, SleepPhasesCard, InsightsCard, GradientIcon
|
||||
│ │ ├── WeeklyChartView.swift # Line chart (not bar), animated
|
||||
│ │ ├── SleepDetailView.swift # Detailed sleep timeline with phases from HealthKit
|
||||
│ │ ├── ReadinessCardView.swift # (empty, replaced by ReadinessBanner in HealthView)
|
||||
│ │ └── ToastView.swift # Toast notification modifier
|
||||
│ ├── Tracker/
|
||||
│ │ └── TrackerView.swift # Tabs: Habits, Tasks, Statistics. Separate tap zones for edit vs toggle
|
||||
│ ├── Tasks/
|
||||
│ │ ├── TasksView.swift
|
||||
│ │ ├── AddTaskView.swift
|
||||
│ │ ├── EditTaskView.swift
|
||||
│ │ └── TaskRowView.swift
|
||||
│ ├── Habits/
|
||||
│ │ ├── HabitsView.swift
|
||||
│ │ ├── AddHabitView.swift
|
||||
│ │ ├── EditHabitView.swift
|
||||
│ │ └── HabitRowView.swift
|
||||
│ ├── Finance/
|
||||
│ │ ├── FinanceView.swift # Overview, Transactions, Analytics, Categories tabs
|
||||
│ │ └── AddTransactionView.swift
|
||||
│ ├── Savings/
|
||||
│ │ ├── SavingsView.swift # Overview (monthly payments, overdues), Categories, Operations
|
||||
│ │ └── EditSavingsCategoryView.swift
|
||||
│ ├── Settings/
|
||||
│ │ └── SettingsView.swift # Appearance, Profile, Timezone (Menu picker)
|
||||
│ └── Profile/
|
||||
│ └── ProfileView.swift # ChangePasswordView
|
||||
```
|
||||
|
||||
## API Integration
|
||||
|
||||
### Main API: `https://api.digital-home.site`
|
||||
- Auth: JWT Bearer token with auto-refresh via `/auth/refresh`
|
||||
- AuthManager stores token + refreshToken in UserDefaults
|
||||
- APIService.authManager weak ref enables transparent 401 → refresh → retry
|
||||
|
||||
**Endpoints used:**
|
||||
- Auth: login, register, me, refresh
|
||||
- Profile: GET/PUT /profile
|
||||
- Tasks: CRUD + complete/uncomplete, priorities 1-3 (not 4!)
|
||||
- Habits: CRUD + log (sends `date` not `completed_at`), freezes, stats
|
||||
- Habits frequency: iOS uses daily/weekly/monthly/interval internally, sends `custom` to API for monthly/interval
|
||||
- Finance: transactions, categories (field is `emoji` not `icon`), summary, analytics
|
||||
- Savings: categories, transactions (date required!), stats (includes monthly_payment_details, overdues), recurring plans
|
||||
|
||||
### Health API: `https://health.digital-home.site`
|
||||
- Separate JWT auth (hardcoded credentials in HealthAPIService)
|
||||
- API key for data sync: `health-cosmo-2026`
|
||||
- Server code: `/Users/daniilklimov/Personal/health-webhook` (Node.js + SQLite)
|
||||
|
||||
**Data flow:**
|
||||
1. Apple Watch → HealthKit on iPhone
|
||||
2. App collects from HealthKit → POST /api/health?key=API_KEY
|
||||
3. health-webhook stores JSON files → parses on GET requests
|
||||
4. App displays from GET /api/health/latest, /readiness, /heatmap
|
||||
|
||||
**HealthKit metrics collected:**
|
||||
- step_count, heart_rate, resting_heart_rate, heart_rate_variability
|
||||
- active_energy (kcal→kJ), blood_oxygen_saturation, walking_running_distance
|
||||
- respiratory_rate, sleep_analysis (with phase breakdown: deep/rem/core/awake + timestamps)
|
||||
|
||||
**Sleep format for webhook:**
|
||||
```json
|
||||
{"totalSleep": 7.5, "deep": 1.2, "rem": 2.0, "core": 4.3, "awake": 0.5,
|
||||
"inBedStart": "...", "sleepEnd": "...", "source": "Apple Watch"}
|
||||
```
|
||||
|
||||
**API response field mapping (CodingKeys):**
|
||||
- `spo2` → `bloodOxygen` (BloodOxygenData)
|
||||
- `respiratoryRate` → as-is
|
||||
- `distance` → as-is
|
||||
- `activeEnergy` → as-is
|
||||
|
||||
## Design System
|
||||
- **Background:** `#06060f` (deep dark)
|
||||
- **Accent:** `#0D9488` (teal)
|
||||
- **Glass cards:** `.glassCard()` modifier — ultraThinMaterial + gradient fill + gradient stroke
|
||||
- **Glow icons:** `GlowIcon` — circle with blur glow behind
|
||||
- **Tab bar:** Custom VStack-based (not standard TabView), each tab has its own glow color
|
||||
- **All sheets:** `.presentationDetents([.large])`, background `Color(hex: "06060f")`
|
||||
- **Color pickers:** LazyVGrid 5 columns (not HStack — overflow on small screens)
|
||||
- **App icon:** Glassmorphism style, Assets.xcassets/AppIcon.appiconset
|
||||
|
||||
## Key Design Decisions & Gotchas
|
||||
- **Buttons in ScrollView/List MUST have `.buttonStyle(.plain)`** — otherwise taps get swallowed
|
||||
- **Tracker rows:** Separate tap zones — `.onTapGesture` on text area for edit, `Button` with `.buttonStyle(.plain)` for checkbox
|
||||
- **`try?` is avoided in save functions** — errors are shown in UI via `@State errorMessage`
|
||||
- **Tab bar uses VStack, not ZStack** — prevents content overlap
|
||||
- **onChange uses iOS 17+ syntax:** `{ }` not `{ _ in }`
|
||||
- **XcodeGen:** All capabilities must be in project.yml, manual Xcode changes get reset
|
||||
|
||||
## Background Sync
|
||||
- BGProcessingTask: `com.daniil.pulsehealth.healthsync`
|
||||
- Scheduled every ~30 minutes
|
||||
- Collects HealthKit data and POSTs to health-webhook
|
||||
- Registered in App.init(), scheduled in .onAppear
|
||||
|
||||
## External Services & Paths
|
||||
- **Pulse API source:** `/Users/daniilklimov/digital-home/pulse-api` (Go)
|
||||
- **Pulse Web source:** `/Users/daniilklimov/digital-home/pulse-web` (React)
|
||||
- **Health webhook:** `/Users/daniilklimov/Personal/health-webhook` (Node.js)
|
||||
- **Infrastructure docs:** `~/Obsidian/daniil/Инфраструктура`
|
||||
|
||||
## Known Issues / TODO
|
||||
- Finance tab is owner-only (user.id === 1) in web, no such restriction in iOS
|
||||
- Savings members endpoints (multi-user) not implemented in iOS
|
||||
- Auth: password change works via direct URLRequest, not through APIService
|
||||
- Health readiness `activity.value` from API shows different step count than latest (different time periods)
|
||||
Reference in New Issue
Block a user