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:
2026-07-17 18:51:49 +02:00
parent 00ae95f5c0
commit 1ae6248fb4
11 changed files with 1218 additions and 11 deletions

View File

@ -87,15 +87,18 @@ final class DriftProgramRepository implements ProgramRepository {
@override
Future<domain.Program?> findById(String id) async {
final row = await (database.select(
database.programs,
)..where((table) => table.id.equals(id))).getSingleOrNull();
final query = database.select(database.programs)
..where((table) => table.id.equals(id) & table.deletedAt.isNull());
final row = await query.getSingleOrNull();
if (row == null) {
return null;
}
final exercises =
await (database.select(database.programExercises)
..where((table) => table.programId.equals(id))
..where(
(table) =>
table.programId.equals(id) & table.deletedAt.isNull(),
)
..orderBy([(table) => OrderingTerm.asc(table.position)]))
.get();
return _programFromRow(
@ -104,6 +107,30 @@ final class DriftProgramRepository implements ProgramRepository {
);
}
@override
Future<List<domain.Program>> listActive() async {
final rows =
await (database.select(database.programs)
..where((table) => table.deletedAt.isNull())
..orderBy([(table) => OrderingTerm.asc(table.name)]))
.get();
final programs = <domain.Program>[];
for (final row in rows) {
final exercises =
await (database.select(database.programExercises)
..where(
(table) =>
table.programId.equals(row.id) & table.deletedAt.isNull(),
)
..orderBy([(table) => OrderingTerm.asc(table.position)]))
.get();
programs.add(
_programFromRow(row, exercises.map(_programExerciseFromRow).toList()),
);
}
return programs;
}
@override
Future<void> save(domain.Program program) async {
await database
@ -120,6 +147,40 @@ final class DriftProgramRepository implements ProgramRepository {
.into(database.programExercises)
.insertOnConflictUpdate(_programExerciseCompanion(exercise));
}
@override
Future<void> replaceExercises(
domain.Program program,
DateTime deletedAt,
) async {
await database.transaction(() async {
await save(program);
final activeIds = program.exercises
.map((exercise) => exercise.metadata.id)
.toSet();
final update = database.update(database.programExercises);
if (activeIds.isEmpty) {
update.where(
(table) =>
table.programId.equals(program.metadata.id) &
table.deletedAt.isNull(),
);
} else {
update.where(
(table) =>
table.programId.equals(program.metadata.id) &
table.deletedAt.isNull() &
table.id.isNotIn(activeIds),
);
}
await update.write(
db.ProgramExercisesCompanion(
deletedAt: Value(deletedAt),
updatedAt: Value(deletedAt),
),
);
});
}
}
final class DriftWorkoutTemplateRepository