feat(02-01): Drift tables, DAOs, scheduling utility, domain models with tests
- Add Rooms, Tasks, TaskCompletions Drift tables with schema v2 migration - Create RoomsDao with CRUD, watchAll, watchWithStats, cascade delete, reorder - Create TasksDao with CRUD, watchInRoom (sorted by due), completeTask, overdue detection - Implement calculateNextDueDate and catchUpToPresent pure scheduling functions - Define IntervalType enum (8 types), EffortLevel enum, FrequencyInterval model - Add formatRelativeDate German formatter and curatedRoomIcons icon list - Enable PRAGMA foreign_keys in beforeOpen migration strategy - All 30 unit tests passing (17 scheduling + 6 rooms DAO + 7 tasks DAO) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,14 +2,75 @@ import 'package:drift/drift.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../../features/rooms/data/rooms_dao.dart';
|
||||
import '../../features/tasks/data/tasks_dao.dart';
|
||||
import '../../features/tasks/domain/effort_level.dart';
|
||||
import '../../features/tasks/domain/frequency.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
@DriftDatabase(tables: [])
|
||||
/// Rooms table: each room has a name, icon, and sort order.
|
||||
class Rooms extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
TextColumn get name => text().withLength(min: 1, max: 100)();
|
||||
TextColumn get iconName => text()();
|
||||
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||
DateTimeColumn get createdAt =>
|
||||
dateTime().clientDefault(() => DateTime.now())();
|
||||
}
|
||||
|
||||
/// Tasks table: each task belongs to a room and has scheduling info.
|
||||
class Tasks extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
IntColumn get roomId => integer().references(Rooms, #id)();
|
||||
TextColumn get name => text().withLength(min: 1, max: 200)();
|
||||
TextColumn get description => text().nullable()();
|
||||
IntColumn get intervalType => intEnum<IntervalType>()();
|
||||
IntColumn get intervalDays =>
|
||||
integer().withDefault(const Constant(1))();
|
||||
IntColumn get anchorDay => integer().nullable()();
|
||||
IntColumn get effortLevel => intEnum<EffortLevel>()();
|
||||
DateTimeColumn get nextDueDate => dateTime()();
|
||||
DateTimeColumn get createdAt =>
|
||||
dateTime().clientDefault(() => DateTime.now())();
|
||||
}
|
||||
|
||||
/// TaskCompletions table: records when a task was completed.
|
||||
class TaskCompletions extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
IntColumn get taskId => integer().references(Tasks, #id)();
|
||||
DateTimeColumn get completedAt => dateTime()();
|
||||
}
|
||||
|
||||
@DriftDatabase(
|
||||
tables: [Rooms, Tasks, TaskCompletions],
|
||||
daos: [RoomsDao, TasksDao],
|
||||
)
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
|
||||
AppDatabase([QueryExecutor? executor])
|
||||
: super(executor ?? _openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
int get schemaVersion => 2;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
return MigrationStrategy(
|
||||
onCreate: (Migrator m) async {
|
||||
await m.createAll();
|
||||
},
|
||||
onUpgrade: (Migrator m, int from, int to) async {
|
||||
if (from < 2) {
|
||||
await m.createTable(rooms);
|
||||
await m.createTable(tasks);
|
||||
await m.createTable(taskCompletions);
|
||||
}
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
await customStatement('PRAGMA foreign_keys = ON');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static QueryExecutor _openConnection() {
|
||||
return driftDatabase(
|
||||
|
||||
Reference in New Issue
Block a user