feat(ui): écran Composition de séance-modèle
Ajoute l'écran de composition de séance-modèle (presentation/workout_template_screen.dart), avec les ajustements application/domaine/repositories nécessaires et la navigation depuis l'accueil. flutter analyze propre, 12/12 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -398,6 +398,14 @@ final class WorkoutTemplateUseCases {
|
||||
final IdGenerator ids;
|
||||
final String originDeviceId;
|
||||
|
||||
Future<WorkoutTemplate?> findById(String id) {
|
||||
return templateRepository.findById(id);
|
||||
}
|
||||
|
||||
Future<List<WorkoutTemplate>> listActive() {
|
||||
return templateRepository.listActive();
|
||||
}
|
||||
|
||||
Future<WorkoutTemplate> create({required String name}) async {
|
||||
final now = clock.now();
|
||||
final template = WorkoutTemplate(
|
||||
@ -408,6 +416,78 @@ final class WorkoutTemplateUseCases {
|
||||
return template;
|
||||
}
|
||||
|
||||
Future<WorkoutTemplate> saveConfigured({
|
||||
String? id,
|
||||
required String name,
|
||||
required List<WorkoutTemplateProgramConfig> programs,
|
||||
required List<WorkoutTemplateExerciseOverrideConfig> overrides,
|
||||
}) async {
|
||||
final now = clock.now();
|
||||
final existing = id == null ? null : await templateRepository.findById(id);
|
||||
if (id != null && existing == null) {
|
||||
throw const DomainException('Workout template not found.');
|
||||
}
|
||||
final templateId = id ?? ids.newId();
|
||||
final metadata =
|
||||
existing?.metadata.touch(now) ??
|
||||
EntityMetadata(
|
||||
id: templateId,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
originDeviceId: originDeviceId,
|
||||
);
|
||||
final templatePrograms = <WorkoutTemplateProgram>[];
|
||||
final programIdsByClientKey = <String, String>{};
|
||||
for (var index = 0; index < programs.length; index++) {
|
||||
final input = programs[index];
|
||||
final programMetadata =
|
||||
input.existingMetadata?.touch(now) ??
|
||||
_newMetadata(ids, originDeviceId, now);
|
||||
programIdsByClientKey[input.clientKey] = programMetadata.id;
|
||||
templatePrograms.add(
|
||||
WorkoutTemplateProgram(
|
||||
metadata: programMetadata,
|
||||
workoutTemplateId: templateId,
|
||||
sourceProgramId: input.sourceProgramId,
|
||||
position: index,
|
||||
programNameSnapshot: input.programNameSnapshot,
|
||||
defaultRestSecondsSnapshot: input.defaultRestSecondsSnapshot,
|
||||
programSnapshotJson: input.programSnapshotJson,
|
||||
),
|
||||
);
|
||||
}
|
||||
final templateOverrides = <WorkoutTemplateExerciseOverride>[];
|
||||
for (final input in overrides) {
|
||||
final workoutTemplateProgramId =
|
||||
programIdsByClientKey[input.workoutTemplateProgramClientKey];
|
||||
if (workoutTemplateProgramId == null) {
|
||||
throw const DomainException('Workout template program not found.');
|
||||
}
|
||||
templateOverrides.add(
|
||||
WorkoutTemplateExerciseOverride(
|
||||
metadata:
|
||||
input.existingMetadata?.touch(now) ??
|
||||
_newMetadata(ids, originDeviceId, now),
|
||||
workoutTemplateProgramId: workoutTemplateProgramId,
|
||||
snapshotProgramExerciseId: input.snapshotProgramExerciseId,
|
||||
setsCountOverride: input.setsCountOverride,
|
||||
targetTimeSecondsOverride: input.targetTimeSecondsOverride,
|
||||
targetRepsOverride: input.targetRepsOverride,
|
||||
targetScoreOverride: input.targetScoreOverride,
|
||||
),
|
||||
);
|
||||
}
|
||||
final template = WorkoutTemplate(
|
||||
metadata: metadata,
|
||||
name: name,
|
||||
lastStartedAt: existing?.lastStartedAt,
|
||||
programs: templatePrograms,
|
||||
overrides: templateOverrides,
|
||||
);
|
||||
await templateRepository.replaceComposition(template, now);
|
||||
return template;
|
||||
}
|
||||
|
||||
Future<WorkoutTemplateProgram> addProgramSnapshot({
|
||||
required String workoutTemplateId,
|
||||
required String programId,
|
||||
@ -457,6 +537,44 @@ final class WorkoutTemplateUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
final class WorkoutTemplateProgramConfig {
|
||||
const WorkoutTemplateProgramConfig({
|
||||
required this.clientKey,
|
||||
this.existingMetadata,
|
||||
this.sourceProgramId,
|
||||
required this.programNameSnapshot,
|
||||
required this.defaultRestSecondsSnapshot,
|
||||
required this.programSnapshotJson,
|
||||
});
|
||||
|
||||
final String clientKey;
|
||||
final EntityMetadata? existingMetadata;
|
||||
final String? sourceProgramId;
|
||||
final String programNameSnapshot;
|
||||
final int defaultRestSecondsSnapshot;
|
||||
final String programSnapshotJson;
|
||||
}
|
||||
|
||||
final class WorkoutTemplateExerciseOverrideConfig {
|
||||
const WorkoutTemplateExerciseOverrideConfig({
|
||||
this.existingMetadata,
|
||||
required this.workoutTemplateProgramClientKey,
|
||||
required this.snapshotProgramExerciseId,
|
||||
this.setsCountOverride,
|
||||
this.targetTimeSecondsOverride,
|
||||
this.targetRepsOverride,
|
||||
this.targetScoreOverride,
|
||||
});
|
||||
|
||||
final EntityMetadata? existingMetadata;
|
||||
final String workoutTemplateProgramClientKey;
|
||||
final String snapshotProgramExerciseId;
|
||||
final int? setsCountOverride;
|
||||
final int? targetTimeSecondsOverride;
|
||||
final int? targetRepsOverride;
|
||||
final double? targetScoreOverride;
|
||||
}
|
||||
|
||||
final class ActiveWorkoutSessionUseCases {
|
||||
const ActiveWorkoutSessionUseCases({
|
||||
required this.sessionRepository,
|
||||
|
||||
Reference in New Issue
Block a user