Ajoute la persistance de l'exécution par étapes avec résultats par passage (ports, use cases, entités, modèle Drift avec migration) et l'éditeur d'exercice avec séquence d'étapes sur exercise_library_screen.dart. Développés dans le même worktree partagé par DevBackend et DevFrontend ; commit combiné plutôt que séparé car les fakes de repository de test corrigés (nouvelles interfaces #58) touchent plusieurs écrans sans lien avec l'éditeur lui-même (historique, accueil, exécution, séance-modèle). Corrige au passage un ListTile sans ancêtre Material dans le nouvel éditeur d'étapes (Material(type: MaterialType.transparency)). flutter pub get OK, build_runner OK, analyze propre (mêmes infos préexistantes), 93/93 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -419,6 +419,108 @@ class ActiveRestStates extends SyncableTable {
|
||||
];
|
||||
}
|
||||
|
||||
class ActiveExerciseStepProgressStates extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'active_exercise_step_progress_states';
|
||||
|
||||
TextColumn get activeWorkoutSessionId =>
|
||||
text().references(ActiveWorkoutSessions, #id)();
|
||||
IntColumn get programIndex => integer()();
|
||||
IntColumn get exerciseIndex => integer()();
|
||||
IntColumn get setIndex => integer()();
|
||||
IntColumn get currentPassageIndex => integer()();
|
||||
IntColumn get currentStepIndex => integer()();
|
||||
TextColumn get currentStepSnapshotId => text().withLength(min: 1)();
|
||||
TextColumn get status => text()();
|
||||
DateTimeColumn get startedAt => dateTime().nullable()();
|
||||
IntColumn get accumulatedMs => integer()();
|
||||
DateTimeColumn get lastTransitionAt => dateTime()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'UNIQUE (active_workout_session_id, program_index, exercise_index, '
|
||||
'set_index)',
|
||||
'CHECK (program_index >= 0)',
|
||||
'CHECK (exercise_index >= 0)',
|
||||
'CHECK (set_index >= 0)',
|
||||
'CHECK (current_passage_index >= 0)',
|
||||
'CHECK (current_step_index >= 0)',
|
||||
"CHECK (status IN ('notStarted', 'waitingManual', 'runningTimer', "
|
||||
"'pausedTimer', 'stoppedTimer', 'sequenceComplete'))",
|
||||
'CHECK (status != \'runningTimer\' OR started_at IS NOT NULL)',
|
||||
'CHECK (accumulated_ms >= 0)',
|
||||
];
|
||||
}
|
||||
|
||||
class ActiveExerciseStepResults extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'active_exercise_step_results';
|
||||
|
||||
TextColumn get activeWorkoutSessionId =>
|
||||
text().references(ActiveWorkoutSessions, #id)();
|
||||
TextColumn get programSnapshotId => text().withLength(min: 1)();
|
||||
TextColumn get exerciseSnapshotId => text().withLength(min: 1)();
|
||||
IntColumn get programIndex => integer()();
|
||||
IntColumn get exerciseIndex => integer()();
|
||||
IntColumn get setIndex => integer()();
|
||||
IntColumn get passageIndex => integer()();
|
||||
IntColumn get stepIndex => integer()();
|
||||
TextColumn get stepSnapshotId => text().withLength(min: 1)();
|
||||
TextColumn get stepNameSnapshot => text().withLength(min: 1)();
|
||||
TextColumn get stepTypeSnapshot => text()();
|
||||
IntColumn get targetValueSnapshot => integer()();
|
||||
BoolColumn get hasScoreSnapshot => boolean()();
|
||||
TextColumn get scoreInputModeSnapshot => text().nullable()();
|
||||
TextColumn get scoreLabelSnapshot => text().nullable()();
|
||||
TextColumn get scoreUnitSnapshot => text().nullable()();
|
||||
RealColumn get targetScoreSnapshot => real().nullable()();
|
||||
IntColumn get targetScoreTimeMsSnapshot => integer().nullable()();
|
||||
TextColumn get status => text()();
|
||||
DateTimeColumn get startedAt => dateTime().nullable()();
|
||||
DateTimeColumn get completedAt => dateTime().nullable()();
|
||||
IntColumn get actualTimeMs => integer().nullable()();
|
||||
IntColumn get actualReps => integer().nullable()();
|
||||
RealColumn get actualScore => real().nullable()();
|
||||
IntColumn get actualScoreTimeMs => integer().nullable()();
|
||||
TextColumn get note => text().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'UNIQUE (active_workout_session_id, program_index, exercise_index, '
|
||||
'set_index, passage_index, step_index)',
|
||||
'CHECK (program_index >= 0)',
|
||||
'CHECK (exercise_index >= 0)',
|
||||
'CHECK (set_index >= 0)',
|
||||
'CHECK (passage_index >= 0)',
|
||||
'CHECK (step_index >= 0)',
|
||||
"CHECK (step_type_snapshot IN ('time', 'reps'))",
|
||||
'CHECK (target_value_snapshot > 0)',
|
||||
"CHECK (score_input_mode_snapshot IS NULL OR "
|
||||
"score_input_mode_snapshot IN ('manual', 'stopwatch'))",
|
||||
"CHECK (status IN ('completed', 'skipped'))",
|
||||
'CHECK (actual_time_ms IS NULL OR actual_time_ms >= 0)',
|
||||
'CHECK (actual_reps IS NULL OR actual_reps >= 0)',
|
||||
'CHECK (actual_score IS NULL OR actual_score >= 0)',
|
||||
'CHECK (actual_score_time_ms IS NULL OR actual_score_time_ms >= 0)',
|
||||
'CHECK (actual_time_ms IS NULL OR step_type_snapshot = \'time\')',
|
||||
'CHECK (actual_reps IS NULL OR step_type_snapshot = \'reps\')',
|
||||
'CHECK (status != \'skipped\' OR (actual_time_ms IS NULL '
|
||||
'AND actual_reps IS NULL AND actual_score IS NULL '
|
||||
'AND actual_score_time_ms IS NULL))',
|
||||
'CHECK (target_score_snapshot IS NULL OR '
|
||||
"(has_score_snapshot AND score_input_mode_snapshot = 'manual'))",
|
||||
'CHECK (target_score_time_ms_snapshot IS NULL OR '
|
||||
"(has_score_snapshot AND score_input_mode_snapshot = 'stopwatch'))",
|
||||
'CHECK (actual_score IS NULL OR '
|
||||
"(has_score_snapshot AND score_input_mode_snapshot = 'manual'))",
|
||||
'CHECK (actual_score_time_ms IS NULL OR '
|
||||
"(has_score_snapshot AND score_input_mode_snapshot = 'stopwatch'))",
|
||||
'CHECK (actual_score IS NULL OR actual_score_time_ms IS NULL)',
|
||||
'CHECK (target_score_snapshot IS NULL OR '
|
||||
'target_score_time_ms_snapshot IS NULL)',
|
||||
];
|
||||
}
|
||||
|
||||
class WorkoutHistories extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'workout_history';
|
||||
@ -511,6 +613,74 @@ class WorkoutHistorySetResults extends SyncableTable {
|
||||
];
|
||||
}
|
||||
|
||||
class WorkoutHistoryStepResults extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'workout_history_step_results';
|
||||
|
||||
TextColumn get workoutHistoryId => text().references(WorkoutHistories, #id)();
|
||||
TextColumn get programSnapshotId => text().withLength(min: 1)();
|
||||
TextColumn get exerciseSnapshotId => text().withLength(min: 1)();
|
||||
IntColumn get programIndex => integer()();
|
||||
IntColumn get exerciseIndex => integer()();
|
||||
IntColumn get setIndex => integer()();
|
||||
IntColumn get passageIndex => integer()();
|
||||
IntColumn get stepIndex => integer()();
|
||||
TextColumn get stepSnapshotId => text().withLength(min: 1)();
|
||||
TextColumn get stepNameSnapshot => text().withLength(min: 1)();
|
||||
TextColumn get stepTypeSnapshot => text()();
|
||||
IntColumn get targetValueSnapshot => integer()();
|
||||
BoolColumn get hasScoreSnapshot => boolean()();
|
||||
TextColumn get scoreInputModeSnapshot => text().nullable()();
|
||||
TextColumn get scoreLabelSnapshot => text().nullable()();
|
||||
TextColumn get scoreUnitSnapshot => text().nullable()();
|
||||
RealColumn get targetScoreSnapshot => real().nullable()();
|
||||
IntColumn get targetScoreTimeMsSnapshot => integer().nullable()();
|
||||
TextColumn get status => text()();
|
||||
DateTimeColumn get startedAt => dateTime().nullable()();
|
||||
DateTimeColumn get completedAt => dateTime().nullable()();
|
||||
IntColumn get actualTimeMs => integer().nullable()();
|
||||
IntColumn get actualReps => integer().nullable()();
|
||||
RealColumn get actualScore => real().nullable()();
|
||||
IntColumn get actualScoreTimeMs => integer().nullable()();
|
||||
TextColumn get note => text().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'UNIQUE (workout_history_id, program_index, exercise_index, set_index, '
|
||||
'passage_index, step_index)',
|
||||
'CHECK (program_index >= 0)',
|
||||
'CHECK (exercise_index >= 0)',
|
||||
'CHECK (set_index >= 0)',
|
||||
'CHECK (passage_index >= 0)',
|
||||
'CHECK (step_index >= 0)',
|
||||
"CHECK (step_type_snapshot IN ('time', 'reps'))",
|
||||
'CHECK (target_value_snapshot > 0)',
|
||||
"CHECK (score_input_mode_snapshot IS NULL OR "
|
||||
"score_input_mode_snapshot IN ('manual', 'stopwatch'))",
|
||||
"CHECK (status IN ('completed', 'skipped'))",
|
||||
'CHECK (actual_time_ms IS NULL OR actual_time_ms >= 0)',
|
||||
'CHECK (actual_reps IS NULL OR actual_reps >= 0)',
|
||||
'CHECK (actual_score IS NULL OR actual_score >= 0)',
|
||||
'CHECK (actual_score_time_ms IS NULL OR actual_score_time_ms >= 0)',
|
||||
'CHECK (actual_time_ms IS NULL OR step_type_snapshot = \'time\')',
|
||||
'CHECK (actual_reps IS NULL OR step_type_snapshot = \'reps\')',
|
||||
'CHECK (status != \'skipped\' OR (actual_time_ms IS NULL '
|
||||
'AND actual_reps IS NULL AND actual_score IS NULL '
|
||||
'AND actual_score_time_ms IS NULL))',
|
||||
'CHECK (target_score_snapshot IS NULL OR '
|
||||
"(has_score_snapshot AND score_input_mode_snapshot = 'manual'))",
|
||||
'CHECK (target_score_time_ms_snapshot IS NULL OR '
|
||||
"(has_score_snapshot AND score_input_mode_snapshot = 'stopwatch'))",
|
||||
'CHECK (actual_score IS NULL OR '
|
||||
"(has_score_snapshot AND score_input_mode_snapshot = 'manual'))",
|
||||
'CHECK (actual_score_time_ms IS NULL OR '
|
||||
"(has_score_snapshot AND score_input_mode_snapshot = 'stopwatch'))",
|
||||
'CHECK (actual_score IS NULL OR actual_score_time_ms IS NULL)',
|
||||
'CHECK (target_score_snapshot IS NULL OR '
|
||||
'target_score_time_ms_snapshot IS NULL)',
|
||||
];
|
||||
}
|
||||
|
||||
class ChangeLogEntries extends Table {
|
||||
@override
|
||||
String get tableName => 'change_log';
|
||||
|
||||
Reference in New Issue
Block a user