feat(exercice): modèle domain + Drift pour exercices à étapes (ticket #56)
Étend le modèle Drift (tables.dart, app_database.dart/.g.dart, migration schemaVersion 7→8) et la couche application (entités, use cases, repositories) pour supporter des exercices composés de plusieurs étapes. flutter pub get OK, build_runner OK, flutter analyze propre (mêmes 8 infos préexistantes), 87/87 tests verts, build APK debug validé. Premier ticket du chantier "Exercice à plusieurs étapes" (#54). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -107,6 +107,50 @@ class ExerciseImages extends SyncableTable {
|
||||
];
|
||||
}
|
||||
|
||||
class ExerciseSteps extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'exercise_steps';
|
||||
|
||||
TextColumn get exerciseId => text().references(Exercises, #id)();
|
||||
IntColumn get position => integer()();
|
||||
TextColumn get name => text().withLength(min: 1)();
|
||||
TextColumn get type => text()();
|
||||
IntColumn get defaultTargetValue => integer()();
|
||||
BoolColumn get hasScore => boolean()();
|
||||
TextColumn get scoreInputMode => text().nullable()();
|
||||
TextColumn get scoreLabel => text().nullable()();
|
||||
TextColumn get scoreUnit => text().nullable()();
|
||||
RealColumn get defaultTargetScore => real().nullable()();
|
||||
IntColumn get defaultTargetScoreTimeMs => integer().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
"CHECK (type IN ('time', 'reps'))",
|
||||
'CHECK (position >= 0 AND position < 8)',
|
||||
'CHECK (default_target_value > 0)',
|
||||
"CHECK (score_input_mode IS NULL OR score_input_mode IN ('manual', "
|
||||
"'stopwatch'))",
|
||||
'CHECK (has_score OR (score_input_mode IS NULL '
|
||||
'AND score_label IS NULL AND score_unit IS NULL '
|
||||
'AND default_target_score IS NULL '
|
||||
'AND default_target_score_time_ms IS NULL))',
|
||||
'CHECK (NOT has_score OR score_input_mode IS NOT NULL)',
|
||||
"CHECK (NOT has_score OR score_input_mode != 'manual' OR "
|
||||
'(score_label IS NOT NULL AND length(trim(score_label)) > 0 '
|
||||
'AND score_unit IS NOT NULL AND length(trim(score_unit)) > 0))',
|
||||
'CHECK (default_target_score IS NULL OR default_target_score >= 0)',
|
||||
'CHECK (default_target_score_time_ms IS NULL OR '
|
||||
'default_target_score_time_ms > 0)',
|
||||
"CHECK (score_input_mode != 'manual' OR "
|
||||
'default_target_score_time_ms IS NULL)',
|
||||
"CHECK (score_input_mode != 'stopwatch' OR "
|
||||
'(score_label IS NULL AND score_unit IS NULL '
|
||||
'AND default_target_score IS NULL))',
|
||||
'CHECK (default_target_score IS NULL OR '
|
||||
'default_target_score_time_ms IS NULL)',
|
||||
];
|
||||
}
|
||||
|
||||
class Programs extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'programs';
|
||||
@ -132,6 +176,7 @@ class ProgramExercises extends SyncableTable {
|
||||
TextColumn get exerciseImageMediaIdSnapshot =>
|
||||
text().nullable().references(MediaAssets, #id)();
|
||||
TextColumn get exerciseImageMediaIdsSnapshotJson => text().nullable()();
|
||||
TextColumn get exerciseStepsSnapshotJson => text().nullable()();
|
||||
|
||||
@ReferenceName('programExerciseVideoSnapshotReferences')
|
||||
TextColumn get exerciseVideoMediaIdSnapshot =>
|
||||
|
||||
Reference in New Issue
Block a user