feat(exercice): valeurs par défaut d'exercice (tickets #39/#40, parent #30)

Étend le modèle Drift (tables.dart, app_database.dart/.g.dart), les
entités/use cases/repositories et les écrans bibliothèque d'exercices
et programme pour supporter des valeurs par défaut par exercice
(répétitions/temps/poids proposés à la création d'une série).
flutter analyze propre, 51/51 tests verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 10:18:31 +02:00
parent 6ee06fef01
commit 88af0387da
14 changed files with 882 additions and 27 deletions

View File

@ -132,6 +132,10 @@ final class Exercise {
this.scoreInputMode = ScoreInputMode.manual,
this.scoreLabel,
this.scoreUnit,
this.defaultTargetTimeSeconds,
this.defaultTargetReps,
this.defaultTargetScore,
this.defaultTargetScoreTimeMs,
this.archivedAt,
}) : name = _nonBlank(name, 'Exercise name') {
_requireAtLeastOneMeasure(
@ -148,6 +152,16 @@ final class Exercise {
_nonBlank(scoreLabel, 'Score label');
_nonBlank(scoreUnit, 'Score unit');
}
_requireNullablePositive(
defaultTargetTimeSeconds,
'Default target time seconds',
);
_requireNullablePositive(defaultTargetReps, 'Default target reps');
_requireNullablePositiveDouble(defaultTargetScore, 'Default target score');
_requireNullablePositive(
defaultTargetScoreTimeMs,
'Default target score time ms',
);
}
final EntityMetadata metadata;
@ -161,6 +175,10 @@ final class Exercise {
final ScoreInputMode scoreInputMode;
final String? scoreLabel;
final String? scoreUnit;
final int? defaultTargetTimeSeconds;
final int? defaultTargetReps;
final double? defaultTargetScore;
final int? defaultTargetScoreTimeMs;
final DateTime? archivedAt;
Set<WorkoutMeasure> get availableMeasures => {
@ -185,6 +203,10 @@ final class Exercise {
ScoreInputMode? scoreInputMode,
Object? scoreLabel = _unchanged,
Object? scoreUnit = _unchanged,
Object? defaultTargetTimeSeconds = _unchanged,
Object? defaultTargetReps = _unchanged,
Object? defaultTargetScore = _unchanged,
Object? defaultTargetScoreTimeMs = _unchanged,
Object? archivedAt = _unchanged,
}) {
return Exercise(
@ -209,6 +231,18 @@ final class Exercise {
scoreUnit: scoreUnit == _unchanged
? this.scoreUnit
: scoreUnit as String?,
defaultTargetTimeSeconds: defaultTargetTimeSeconds == _unchanged
? this.defaultTargetTimeSeconds
: defaultTargetTimeSeconds as int?,
defaultTargetReps: defaultTargetReps == _unchanged
? this.defaultTargetReps
: defaultTargetReps as int?,
defaultTargetScore: defaultTargetScore == _unchanged
? this.defaultTargetScore
: defaultTargetScore as double?,
defaultTargetScoreTimeMs: defaultTargetScoreTimeMs == _unchanged
? this.defaultTargetScoreTimeMs
: defaultTargetScoreTimeMs as int?,
archivedAt: archivedAt == _unchanged
? this.archivedAt
: archivedAt as DateTime?,
@ -371,10 +405,10 @@ final class ProgramExercise {
timeEnabled: enabledMeasures.contains(WorkoutMeasure.time),
repsEnabled: enabledMeasures.contains(WorkoutMeasure.reps),
scoreEnabled: enabledMeasures.contains(WorkoutMeasure.score),
targetTimeSeconds: targetTimeSeconds,
targetReps: targetReps,
targetScore: targetScore,
targetScoreTimeMs: targetScoreTimeMs,
targetTimeSeconds: targetTimeSeconds ?? exercise.defaultTargetTimeSeconds,
targetReps: targetReps ?? exercise.defaultTargetReps,
targetScore: targetScore ?? exercise.defaultTargetScore,
targetScoreTimeMs: targetScoreTimeMs ?? exercise.defaultTargetScoreTimeMs,
restSecondsOverride: restSecondsOverride,
);
}
@ -927,6 +961,12 @@ void _requireNullableNonNegativeDouble(double? value, String label) {
}
}
void _requireNullablePositiveDouble(double? value, String label) {
if (value != null && value <= 0) {
throw DomainException('$label must be positive.');
}
}
void _requireScoreTargetShape({
required bool scoreEnabled,
required ScoreInputMode scoreInputMode,