import 'package:flutter/material.dart'; import 'package:manager_api_new/api.dart'; import 'package:manager_app/constants.dart'; import 'package:manager_app/l10n/app_localizations.dart'; import 'package:manager_app/Components/check_input_container.dart'; import 'package:manager_app/Components/multi_string_input_container.dart'; import 'package:manager_app/Components/number_stepper_field.dart'; import 'package:manager_app/Components/resource_input_container.dart'; import 'package:manager_app/Components/section_card.dart'; import '../progression_mode.dart'; /// Les champs propres au parcours, sans son contenant ni sa liste d'étapes. /// /// Le DTO est modifié en place ; `onChanged` prévient l'hôte, à charge pour lui /// de se reconstruire et de déclencher l'enregistrement. class ParcoursFields extends StatelessWidget { const ParcoursFields({ Key? key, required this.path, required this.onChanged, }) : super(key: key); final GuidedPathDTO path; final VoidCallback onChanged; /// Réécrit une liste de traductions en conservant la ressource déjà associée /// à chaque langue : `MultiStringInputContainer` ne manipule que le texte. List _mergeResources( List values, List? previous, ) { return values.map((t) { final prev = (previous ?? []).firstWhere( (e) => e.language == t.language, orElse: () => TranslationAndResourceDTO(), ); return TranslationAndResourceDTO( language: t.language, value: t.value, resourceId: prev.resourceId, resource: prev.resource, ); }).toList(); } @override Widget build(BuildContext context) { final l = AppLocalizations.of(context)!; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ResourceInputContainer( label: l.imageLabel, color: kPrimaryColor, initialValue: path.imageResourceId, onChanged: (resourceDTO) { path.imageResourceId = resourceDTO.id; onChanged(); }, ), SizedBox(height: kSpace5), Row( children: [ Expanded( child: MultiStringInputContainer( label: l.titleColonLabel, modalLabel: l.pathTitleLabel, initialValue: path.title ?? [], onGetResult: (val) { path.title = val; onChanged(); }, maxLines: 1, isTitle: true, isHTML: true, showPreview: true, ), ), SizedBox(width: kSpace7), Expanded( child: MultiStringInputContainer( label: l.descriptionColonLabel, modalLabel: l.pathDescriptionLabel, initialValue: path.description ?? [], onGetResult: (val) { path.description = val; onChanged(); }, maxLines: 1, isTitle: false, isHTML: true, showPreview: true, ), ), ], ), SizedBox(height: kSpace5), SectionCard( icon: Icons.sports_esports, title: l.pathMoodTitle, subtitle: l.pathMoodSub, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l.pathMoodQuestion, style: kLabelField), RadioGroup( groupValue: path.isGameMode ?? false, onChanged: (val) { path.isGameMode = val; onChanged(); }, child: Column( children: [ RadioListTile( dense: true, contentPadding: EdgeInsets.zero, activeColor: kPrimaryColor, value: false, title: Text(l.pathMoodVisit, style: kTextBody), subtitle: Text( l.pathMoodVisitDesc, style: kTextHint, ), ), RadioListTile( dense: true, contentPadding: EdgeInsets.zero, activeColor: kPrimaryColor, value: true, title: Text(l.pathMoodGame, style: kTextBody), subtitle: Text( l.pathMoodGameDesc, style: kTextHint, ), ), ], ), ), if (path.isGameMode == true) ...[ SizedBox(height: kSpace3), Row( children: [ Expanded( child: MultiStringInputContainer( label: l.startMessageLabel, modalLabel: l.gameStartMessageModalLabel, color: kPrimaryColor, initialValue: (path.gameMessageDebut ?? []) .map((t) => TranslationDTO( language: t.language, value: t.value)) .toList(), onGetResult: (val) { path.gameMessageDebut = _mergeResources(val, path.gameMessageDebut); onChanged(); }, maxLines: 2, isTitle: false, isHTML: true, showPreview: true, ), ), SizedBox(width: kSpace7), Expanded( child: MultiStringInputContainer( label: l.endMessageLabel, modalLabel: l.gameEndMessageModalLabel, color: kPrimaryColor, initialValue: (path.gameMessageFin ?? []) .map((t) => TranslationDTO( language: t.language, value: t.value)) .toList(), onGetResult: (val) { path.gameMessageFin = _mergeResources(val, path.gameMessageFin); onChanged(); }, maxLines: 2, isTitle: false, isHTML: true, showPreview: true, ), ), ], ), ], ], ), ), SizedBox(height: kSpace5), SectionCard( icon: Icons.settings, title: l.pathOptionsTitle, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l.pathProgressionQuestion, style: kLabelField), RadioGroup( groupValue: progressionModeOf(path), onChanged: (val) { applyProgressionMode(path, val!); onChanged(); }, child: Column( children: [ for (final mode in ProgressionMode.values) RadioListTile( dense: true, contentPadding: EdgeInsets.zero, activeColor: kPrimaryColor, value: mode, title: Text(mode.label(l), style: kTextBody), subtitle: Text(mode.description(l), style: kTextHint), ), ], ), ), if (progressionModeOf(path) == ProgressionMode.stepByStep) Padding( padding: const EdgeInsets.only(left: kSpace5), child: CheckInputContainer( label: l.pathHideNextSteps, subtitle: l.pathHideNextStepsDesc, isChecked: path.hideNextStepsUntilComplete ?? false, onChanged: (val) { path.hideNextStepsUntilComplete = val; onChanged(); }, ), ), SizedBox(height: kSpace3), NumberStepperField( label: l.pathEstimatedDuration, value: path.estimatedDurationMinutes, min: 0, max: 600, unit: l.minutesAbbr, onChanged: (val) { path.estimatedDurationMinutes = val.toInt(); onChanged(); }, ), ], ), ), ], ); } }