Skip to content

Commit 69d4e68

Browse files
committed
♻️ refactor: implement local date/time storage for measurements and restructure documentation
- Changed measurement storage from Unix timestamps to local date/time strings - Withings now converts UTC timestamps to local time before storage - Fitbit measurements stored as-is (already in local timezone) - Updated RawMeasurement model to use separate date and time fields - Moved architectural details from README.md to new ARCHITECTURE.md - Simplified README.md to focus on key information for GitHub visitors - Updated CLAUDE.md with timestamp storage details and ProfileService refactoring - Frontend now parses date/time strings instead of timestamps
1 parent 7a9a6b6 commit 69d4e68

10 files changed

Lines changed: 649 additions & 346 deletions

File tree

ARCHITECTURE.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Architecture
2+
3+
## Overview
4+
5+
TrendWeight uses a modern web architecture with a C# ASP.NET Core API backend and a Vite + React frontend.
6+
7+
## Backend Architecture
8+
9+
### Technology Stack
10+
- **ASP.NET Core 9.0** Web API
11+
- **Supabase** (PostgreSQL) for data storage
12+
- **JWT-based authentication**
13+
- **Feature-based architecture** with service layer pattern
14+
15+
### Service Layer Pattern
16+
17+
The backend uses a service layer pattern to separate business logic from controllers:
18+
- **ProfileService**: Manages user profile CRUD operations and business logic
19+
- **ProviderService**: Base class for provider-specific implementations (Withings/Fitbit)
20+
- **SourceDataService**: Handles raw measurement data storage and retrieval
21+
- **MeasurementsController**: Orchestrates data fetching from multiple providers
22+
23+
Controllers should be thin, delegating complex logic to services.
24+
25+
### Project Structure
26+
```
27+
apps/api/
28+
└── TrendWeight/
29+
├── Features/ # Feature-based organization
30+
│ ├── Profile/ # User profile management
31+
│ ├── Providers/ # Withings/Fitbit integrations
32+
│ └── Measurements/ # Weight data management
33+
└── Infrastructure/ # Cross-cutting concerns
34+
```
35+
36+
## Frontend Architecture
37+
38+
### Technology Stack
39+
- **React 19** with TypeScript
40+
- **Vite** for build tooling
41+
- **TanStack Router** for routing
42+
- **TanStack Query** for server state management
43+
- **Tailwind CSS v4** for styling
44+
- **Radix UI** for accessible components
45+
46+
## Timestamp Storage
47+
48+
The application stores weight measurements with local date and time strings rather than Unix timestamps:
49+
50+
### Storage Format
51+
```typescript
52+
interface RawMeasurement {
53+
date: string; // "2024-01-15" (YYYY-MM-DD)
54+
time: string; // "06:30:00" (HH:mm:ss)
55+
weight: number; // kg
56+
fatRatio?: number; // 0-1 ratio
57+
}
58+
```
59+
60+
### Provider Handling
61+
- **Withings**: Provides UTC timestamps with timezone info. The backend converts these to local time before storage.
62+
- **Fitbit**: Provides measurements already in the user's local timezone, stored as-is.
63+
64+
This approach:
65+
- Preserves the user's local time context
66+
- Avoids timezone conversion issues
67+
- Simplifies display logic on the frontend
68+
- Handles provider differences transparently
69+
70+
## Database Schema
71+
72+
### Tables
73+
- **profiles**: User profiles with settings (UUID primary key)
74+
- **provider_links**: OAuth tokens for provider integrations
75+
- **source_data**: Raw measurement data from providers
76+
77+
### Key Principles
78+
- All timestamps stored as ISO 8601 strings
79+
- All weights stored in kg for consistency
80+
- JSONB columns for flexible data storage
81+
- No unique constraint on email (uses Supabase Auth UID)
82+
83+
## Authentication Flow
84+
85+
1. Frontend uses Supabase Auth for user authentication
86+
2. Backend validates Supabase JWTs using shared JWT secret
87+
3. All API endpoints require authentication except health checks and OAuth callbacks
88+
4. Provider OAuth tokens stored encrypted in database
89+
90+
## Development Guidelines
91+
92+
### Backend
93+
- Feature folders for organization
94+
- Dependency injection for all services
95+
- Async/await for all I/O operations
96+
- Nullable fields should remain null when not provided (never default to 0)
97+
98+
### Frontend
99+
- Functional components with hooks
100+
- Custom hooks for reusable logic
101+
- Error boundaries for graceful error handling
102+
- Route-level authentication guards
103+
104+
## Provider Integration Notes
105+
106+
### Fitbit Limitations
107+
- Weight data requests cannot start before 2009-01-01
108+
- Maximum date range per request is 32 days
109+
- Always use initial sync date of 2009-01-01
110+
111+
### Token Management
112+
- Tokens automatically refresh when expired
113+
- Provider userid fields vary in type and are not used
114+
- Failed auth triggers reconnection flow

0 commit comments

Comments
 (0)