feat(ui): écran Création/édition de programme
Ajoute l'écran de programme (presentation/program_screen.dart) et un écran d'accueil (presentation/home_screen.dart) pour la navigation, avec les ajustements application/domaine/repositories nécessaires. flutter analyze propre, 11/11 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -227,6 +227,10 @@ final class ProgramUseCases {
|
||||
final IdGenerator ids;
|
||||
final String originDeviceId;
|
||||
|
||||
Future<Program?> findById(String id) => programRepository.findById(id);
|
||||
|
||||
Future<List<Program>> listActive() => programRepository.listActive();
|
||||
|
||||
Future<Program> create({
|
||||
required String name,
|
||||
required int defaultRestSeconds,
|
||||
@ -241,6 +245,69 @@ final class ProgramUseCases {
|
||||
return program;
|
||||
}
|
||||
|
||||
Future<Program> saveConfigured({
|
||||
String? id,
|
||||
required String name,
|
||||
required int defaultRestSeconds,
|
||||
required List<ProgramExerciseConfig> exercises,
|
||||
}) async {
|
||||
final now = clock.now();
|
||||
final existing = id == null ? null : await programRepository.findById(id);
|
||||
if (id != null && existing == null) {
|
||||
throw const DomainException('Program not found.');
|
||||
}
|
||||
final programId = id ?? ids.newId();
|
||||
final metadata =
|
||||
existing?.metadata.touch(now) ??
|
||||
EntityMetadata(
|
||||
id: programId,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
originDeviceId: originDeviceId,
|
||||
);
|
||||
final programExercises = <ProgramExercise>[];
|
||||
for (var index = 0; index < exercises.length; index++) {
|
||||
final input = exercises[index];
|
||||
final exerciseMetadata =
|
||||
input.existingMetadata?.touch(now) ??
|
||||
_newMetadata(ids, originDeviceId, now);
|
||||
programExercises.add(
|
||||
ProgramExercise(
|
||||
metadata: exerciseMetadata,
|
||||
programId: programId,
|
||||
sourceExerciseId: input.sourceExerciseId,
|
||||
position: index,
|
||||
exerciseNameSnapshot: input.exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: input.exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: input.exerciseImageMediaIdSnapshot,
|
||||
exerciseVideoMediaIdSnapshot: input.exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: input.exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: input.availableTimeSnapshot,
|
||||
availableRepsSnapshot: input.availableRepsSnapshot,
|
||||
availableScoreSnapshot: input.availableScoreSnapshot,
|
||||
scoreLabelSnapshot: input.scoreLabelSnapshot,
|
||||
scoreUnitSnapshot: input.scoreUnitSnapshot,
|
||||
setsCount: input.setsCount,
|
||||
timeEnabled: input.enabledMeasures.contains(WorkoutMeasure.time),
|
||||
repsEnabled: input.enabledMeasures.contains(WorkoutMeasure.reps),
|
||||
scoreEnabled: input.enabledMeasures.contains(WorkoutMeasure.score),
|
||||
targetTimeSeconds: input.targetTimeSeconds,
|
||||
targetReps: input.targetReps,
|
||||
targetScore: input.targetScore,
|
||||
restSecondsOverride: input.restSecondsOverride,
|
||||
),
|
||||
);
|
||||
}
|
||||
final program = Program(
|
||||
metadata: metadata,
|
||||
name: name,
|
||||
defaultRestSeconds: defaultRestSeconds,
|
||||
exercises: programExercises,
|
||||
);
|
||||
await programRepository.replaceExercises(program, now);
|
||||
return program;
|
||||
}
|
||||
|
||||
Future<ProgramExercise> addExercise({
|
||||
required String programId,
|
||||
required String exerciseId,
|
||||
@ -274,6 +341,48 @@ final class ProgramUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
final class ProgramExerciseConfig {
|
||||
const ProgramExerciseConfig({
|
||||
this.existingMetadata,
|
||||
this.sourceExerciseId,
|
||||
required this.exerciseNameSnapshot,
|
||||
this.exerciseDescriptionSnapshot,
|
||||
this.exerciseImageMediaIdSnapshot,
|
||||
this.exerciseVideoMediaIdSnapshot,
|
||||
this.exerciseArchivedSnapshot = false,
|
||||
required this.availableTimeSnapshot,
|
||||
required this.availableRepsSnapshot,
|
||||
required this.availableScoreSnapshot,
|
||||
this.scoreLabelSnapshot,
|
||||
this.scoreUnitSnapshot,
|
||||
required this.setsCount,
|
||||
required this.enabledMeasures,
|
||||
this.targetTimeSeconds,
|
||||
this.targetReps,
|
||||
this.targetScore,
|
||||
this.restSecondsOverride,
|
||||
});
|
||||
|
||||
final EntityMetadata? existingMetadata;
|
||||
final String? sourceExerciseId;
|
||||
final String exerciseNameSnapshot;
|
||||
final String? exerciseDescriptionSnapshot;
|
||||
final String? exerciseImageMediaIdSnapshot;
|
||||
final String? exerciseVideoMediaIdSnapshot;
|
||||
final bool exerciseArchivedSnapshot;
|
||||
final bool availableTimeSnapshot;
|
||||
final bool availableRepsSnapshot;
|
||||
final bool availableScoreSnapshot;
|
||||
final String? scoreLabelSnapshot;
|
||||
final String? scoreUnitSnapshot;
|
||||
final int setsCount;
|
||||
final Set<WorkoutMeasure> enabledMeasures;
|
||||
final int? targetTimeSeconds;
|
||||
final int? targetReps;
|
||||
final double? targetScore;
|
||||
final int? restSecondsOverride;
|
||||
}
|
||||
|
||||
final class WorkoutTemplateUseCases {
|
||||
const WorkoutTemplateUseCases({
|
||||
required this.templateRepository,
|
||||
|
||||
Reference in New Issue
Block a user