92 lines
3.2 KiB
Markdown
92 lines
3.2 KiB
Markdown
# TT-004 — Repository Implementations
|
|
|
|
**Type:** Story
|
|
**Priority:** High
|
|
**Labels:** data-layer, repositories
|
|
**Depends on:** TT-002, TT-003
|
|
**Blocks:** TT-005
|
|
|
|
---
|
|
|
|
## Summary
|
|
Implement the concrete repository classes for each feature. Repositories act
|
|
as the single access point for the data layer; they delegate to Drift DAOs
|
|
and map DB rows to domain models.
|
|
|
|
## Repositories to implement
|
|
|
|
### `ProjectsRepository`
|
|
File: `lib/features/projects/data/projects_repository.dart`
|
|
|
|
```dart
|
|
abstract class ProjectsRepository {
|
|
Stream<List<Project>> watchAll();
|
|
Stream<List<Project>> watchActive(); // archivedAt IS NULL
|
|
Future<Project> getById(int id);
|
|
Future<int> create({required String name, required int colorValue, String? description});
|
|
Future<void> update(Project project);
|
|
Future<void> archive(int id); // sets archivedAt = now
|
|
Future<void> delete(int id);
|
|
}
|
|
```
|
|
|
|
### `TimeEntriesRepository`
|
|
File: `lib/features/entries/data/entries_repository.dart`
|
|
|
|
```dart
|
|
abstract class TimeEntriesRepository {
|
|
Stream<List<TimeEntry>> watchAll();
|
|
Stream<List<TimeEntry>> watchByProject(int projectId);
|
|
Stream<List<TimeEntry>> watchByDateRange(DateTime from, DateTime to);
|
|
Future<TimeEntry?> getActiveEntry(); // end_time IS NULL
|
|
Future<int> create(TimeEntry entry);
|
|
Future<void> update(TimeEntry entry);
|
|
Future<void> delete(int id);
|
|
}
|
|
```
|
|
|
|
### `TagsRepository`
|
|
File: `lib/features/entries/data/tags_repository.dart`
|
|
|
|
```dart
|
|
abstract class TagsRepository {
|
|
Stream<List<Tag>> watchAll();
|
|
Future<Tag> findOrCreate(String name);
|
|
Future<void> setTagsForEntry(int entryId, List<String> tagNames);
|
|
Future<List<Tag>> getTagsForEntry(int entryId);
|
|
}
|
|
```
|
|
|
|
### `ReportsRepository`
|
|
File: `lib/features/reports/data/reports_repository.dart`
|
|
|
|
```dart
|
|
abstract class ReportsRepository {
|
|
Future<Duration> getTotalDuration(DateTime from, DateTime to, {int? projectId});
|
|
Future<Map<int, Duration>> getDurationByProject(DateTime from, DateTime to);
|
|
Future<Map<int, Duration>> getDurationByWeekday(DateTime weekStart);
|
|
Future<Map<int, Duration>> getDurationByDay(DateTime monthStart);
|
|
}
|
|
```
|
|
|
|
## Acceptance Criteria
|
|
- [ ] Abstract interface + concrete `DriftXxxRepository` implementation for each
|
|
- [ ] Riverpod provider for each concrete implementation
|
|
- [ ] All DB access goes through DAOs — no raw SQL in repositories
|
|
- [ ] Mapper extension methods convert `Drift row → domain model`
|
|
- [ ] Unit tests for each repository method using in-memory Drift DB
|
|
|
|
## Files to create / modify
|
|
- `lib/features/projects/data/projects_repository.dart` (replace stub)
|
|
- `lib/features/projects/data/drift_projects_repository.dart`
|
|
- `lib/features/entries/data/entries_repository.dart` (replace stub)
|
|
- `lib/features/entries/data/drift_entries_repository.dart`
|
|
- `lib/features/entries/data/tags_repository.dart`
|
|
- `lib/features/entries/data/drift_tags_repository.dart`
|
|
- `lib/features/reports/data/reports_repository.dart` (replace stub)
|
|
- `lib/features/reports/data/drift_reports_repository.dart`
|
|
- `test/features/*/` — unit tests for each
|
|
|
|
## Notes
|
|
- Keep repositories free of Flutter/UI imports
|
|
- Streams from Drift are reactive — repositories expose them directly to Riverpod providers
|