Files
pulse-mobile/CLAUDE.md
Daniil Klimov 28fca1de89 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>
2026-04-05 23:15:36 +03:00

147 lines
7.7 KiB
Markdown
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.
# 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)