feat(ui): integre affichage stats par scope et selection des types metier d'exercice cote telephone
UI telephone pour #179 (graphes/statistiques par etape, serie, exercice, seance) et #183 (choix des types metier d'exercice, hors pont Health Services). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -361,7 +361,7 @@ final class ExerciseListTile extends StatelessWidget {
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
children: [
|
||||
_CategoryBadge(category: exercise.category),
|
||||
_BusinessTypeBadge(types: exercise.effectiveBusinessTypes),
|
||||
...exercise.availableMeasures.map(
|
||||
(measure) => MeasureBadge(
|
||||
measure: measure,
|
||||
@ -398,17 +398,19 @@ final class ExerciseListTile extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
final class _CategoryBadge extends StatelessWidget {
|
||||
const _CategoryBadge({required this.category});
|
||||
final class _BusinessTypeBadge extends StatelessWidget {
|
||||
const _BusinessTypeBadge({required this.types});
|
||||
|
||||
final ExerciseCategory category;
|
||||
final List<BusinessExerciseType> types;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Chip(
|
||||
visualDensity: VisualDensity.compact,
|
||||
label: Text(category.label),
|
||||
);
|
||||
final label = types.isEmpty
|
||||
? 'Libre / autre'
|
||||
: types.length == 1
|
||||
? types.single.label
|
||||
: '${types.first.label} +${types.length - 1}';
|
||||
return Chip(visualDensity: VisualDensity.compact, label: Text(label));
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,6 +489,7 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
||||
var _hasReps = false;
|
||||
var _hasScore = false;
|
||||
var _scoreInputMode = ScoreInputMode.manual;
|
||||
var _businessTypes = <BusinessExerciseType>[];
|
||||
var _saving = false;
|
||||
var _importingImage = false;
|
||||
var _importingVideo = false;
|
||||
@ -529,6 +532,9 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
||||
_hasReps = exercise?.hasRepsMeasure ?? false;
|
||||
_hasScore = exercise?.hasScoreMeasure ?? false;
|
||||
_scoreInputMode = exercise?.scoreInputMode ?? ScoreInputMode.manual;
|
||||
_businessTypes = List<BusinessExerciseType>.of(
|
||||
exercise?.effectiveBusinessTypes ?? const [],
|
||||
);
|
||||
_stepsEnabled = exercise?.steps.isNotEmpty ?? false;
|
||||
_autoStartNextTimedStep = exercise?.autoStartNextTimedStep ?? true;
|
||||
_tags = List<String>.of(exercise?.tags ?? const []);
|
||||
@ -781,6 +787,8 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
||||
],
|
||||
const SizedBox(height: 24),
|
||||
_buildExerciseStepsSection(context),
|
||||
const SizedBox(height: 16),
|
||||
_buildBusinessTypesSection(context),
|
||||
if (_warning != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
MaterialBanner(
|
||||
@ -811,6 +819,51 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBusinessTypesSection(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Type d’exercice', style: Theme.of(context).textTheme.titleSmall),
|
||||
const SizedBox(height: 8),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
FilterChip(
|
||||
label: const Text('Libre / autre'),
|
||||
selected: _businessTypes.isEmpty,
|
||||
onSelected: (_) => setState(() => _businessTypes = []),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
for (final type in BusinessExerciseType.values) ...[
|
||||
FilterChip(
|
||||
label: Text(type.label),
|
||||
selected: _businessTypes.contains(type),
|
||||
onSelected: (selected) {
|
||||
setState(() {
|
||||
final next = List<BusinessExerciseType>.of(
|
||||
_businessTypes,
|
||||
);
|
||||
if (selected) {
|
||||
if (!next.contains(type)) {
|
||||
next.add(type);
|
||||
}
|
||||
} else {
|
||||
next.remove(type);
|
||||
}
|
||||
_businessTypes = next;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExerciseStepsSection(BuildContext context) {
|
||||
final reachedLimit = _stepDrafts.length >= 8;
|
||||
return Column(
|
||||
@ -1467,6 +1520,7 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
||||
defaultTargetScore: defaultTargetScore,
|
||||
defaultTargetScoreTimeMs: defaultTargetScoreTimeMs,
|
||||
autoStartNextTimedStep: _autoStartNextTimedStep,
|
||||
businessTypes: _businessTypes,
|
||||
steps: steps,
|
||||
tags: _tags,
|
||||
);
|
||||
@ -1489,6 +1543,7 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
||||
defaultTargetScore: defaultTargetScore,
|
||||
defaultTargetScoreTimeMs: defaultTargetScoreTimeMs,
|
||||
autoStartNextTimedStep: _autoStartNextTimedStep,
|
||||
businessTypes: _businessTypes,
|
||||
steps: steps,
|
||||
tags: _tags,
|
||||
);
|
||||
@ -2022,17 +2077,18 @@ extension on WorkoutMeasure {
|
||||
}
|
||||
}
|
||||
|
||||
extension on ExerciseCategory {
|
||||
extension on BusinessExerciseType {
|
||||
String get label {
|
||||
return switch (this) {
|
||||
ExerciseCategory.shoot => 'Tir',
|
||||
ExerciseCategory.freeThrows => 'Lancers francs',
|
||||
ExerciseCategory.dribble => 'Dribble',
|
||||
ExerciseCategory.finishing => 'Finition',
|
||||
ExerciseCategory.conditioning => 'Condition physique',
|
||||
ExerciseCategory.defense => 'Défense',
|
||||
ExerciseCategory.mobility => 'Mobilité',
|
||||
ExerciseCategory.uncategorized => 'Non catégorisé',
|
||||
BusinessExerciseType.shoot => 'Tir',
|
||||
BusinessExerciseType.freeThrows => 'Lancers francs',
|
||||
BusinessExerciseType.dribble => 'Dribble',
|
||||
BusinessExerciseType.finishing => 'Finition',
|
||||
BusinessExerciseType.conditioning => 'Condition physique',
|
||||
BusinessExerciseType.defense => 'Défense',
|
||||
BusinessExerciseType.mobility => 'Mobilité',
|
||||
BusinessExerciseType.highIntensity => 'Haute intensité',
|
||||
BusinessExerciseType.recovery => 'Récupération',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user