TimeRecord Model
Definition
struct TimeRecord: Identifiable, Codable {
let id: UUID
var startTime: Date
var endTime: Date? // nil = running timer
var tagId: UUID? // nil = untagged
var comment: String?
var images: [String] // filenames of attached images
} JSON Format
{
"id": "uuid-string",
"startTime": "2024-01-15T09:30:00Z",
"endTime": "2024-01-15T11:45:00Z",
"tagId": "uuid-string",
"comment": "Working on feature X",
"images": ["uuid-string_0.jpg", "uuid-string_1.jpg"]
} Note: The images field is optional in JSON for backward compatibility. Old records without the field will decode with an empty array.
File Naming
YYYY-MM-DD_HHmmss.json based on startTime
Storage Path
records/YYYY/MM/YYYY-MM-DD_HHmmss.json
Business Rules
- Multiple concurrent running timers allowed
- Overlapping records are valid
- tagId is optional (untagged records valid)
- endTime is optional (nil = running)
- images array is optional (old records without images field decode with empty array)
Image Storage
Images are stored in the same directory as the JSON record:
- Path:
records/YYYY/MM/(same as record JSON) - Naming:
{record-id}_{index}.{ext}- Example:
550e8400-e29b-41d4-a716-446655440000_0.jpg
- Example:
- Supported formats: JPEG, PNG, HEIF
- Processing: Resized to max 1920px width/height, compressed to 80% JPEG quality
- When a record is deleted, all its images are automatically deleted
Related
Data
- 201-tag - Tag reference for categorization
Services
- 301-file-storage - CRUD and image operations
- 303-time-tracking - Timer start/stop, record updates
UI
- 406-record-editor - Edit record fields and images
- 407-history-section - Record listing with filtering
- 412-deletion-toast - Soft delete with undo
Reports
- 306-report - ReportRecordItem, ReportCommentItem
- 309-comments-section - Comment and image display