import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:manager_api_new/api.dart'; import 'package:manager_app/constants.dart'; import 'package:manager_app/Components/rounded_button.dart'; import 'package:manager_app/Components/multi_string_input_container.dart'; import 'package:manager_app/Components/check_input_container.dart'; import 'showNewOrUpdateGuidedStep.dart'; void showNewOrUpdateGuidedPath( BuildContext context, GuidedPathDTO? path, String parentId, bool isEvent, bool isEscapeMode, FutureOr Function(GuidedPathDTO) onSave, ) { GuidedPathDTO workingPath = path != null ? GuidedPathDTO.fromJson(jsonDecode(jsonEncode(path)))! : GuidedPathDTO( title: [], description: [], steps: [], order: 0, ); bool isSaving = false; showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return StatefulBuilder( builder: (context, setState) { final double screenWidth = MediaQuery.of(context).size.width; final double screenHeight = MediaQuery.of(context).size.height; final double dialogWidth = screenWidth * 0.82; // contentWidth = dialogWidth minus the 24px padding on each side final double contentWidth = dialogWidth - 48; final double halfWidth = (contentWidth - 20) / 2; final double thirdWidth = (contentWidth - 40) / 3; return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Container( width: dialogWidth, constraints: BoxConstraints(maxHeight: screenHeight * 0.88), padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // -- Titre du dialog -- Text( path == null ? "Nouveau Parcours" : "Modifier le Parcours", style: TextStyle( color: kPrimaryColor, fontSize: 20, fontWeight: FontWeight.bold), ), SizedBox(height: 16), // -- Corps scrollable -- Flexible( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Titre + Description côte à côte Row( children: [ SizedBox( width: halfWidth, child: MultiStringInputContainer( label: "Titre :", modalLabel: "Titre du parcours", initialValue: workingPath.title ?? [], onGetResult: (val) => setState(() => workingPath.title = val), maxLines: 1, isTitle: true, isHTML: true, ), ), SizedBox(width: 20), SizedBox( width: halfWidth, child: MultiStringInputContainer( label: "Description :", modalLabel: "Description du parcours", initialValue: workingPath.description ?? [], onGetResult: (val) => setState( () => workingPath.description = val), maxLines: 1, isTitle: false, isHTML: true, ), ), ], ), Divider(height: 24), // Options Row( children: [ SizedBox( width: thirdWidth, child: CheckInputContainer( label: "Linéaire :", isChecked: workingPath.isLinear ?? false, onChanged: (val) => setState( () => workingPath.isLinear = val), ), ), SizedBox(width: 20), SizedBox( width: thirdWidth, child: CheckInputContainer( label: "Réussite requise :", isChecked: workingPath.requireSuccessToAdvance ?? false, onChanged: (val) => setState(() => workingPath .requireSuccessToAdvance = val), ), ), SizedBox(width: 20), SizedBox( width: thirdWidth, child: CheckInputContainer( label: "Cacher les suivantes :", isChecked: workingPath.hideNextStepsUntilComplete ?? false, onChanged: (val) => setState(() => workingPath .hideNextStepsUntilComplete = val), ), ), ], ), Divider(height: 24), // Étapes Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Étapes du parcours", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15)), IconButton( icon: Icon(Icons.add_circle_outline, color: kSuccess), onPressed: () { showNewOrUpdateGuidedStep( context, null, workingPath.id ?? "temp", isEscapeMode, (newStep) async { setState(() { workingPath.steps = [ ...(workingPath.steps ?? []), newStep ]; }); }, ); }, ), ], ), if (workingPath.steps?.isEmpty ?? true) Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Text( "Aucun point/étape configuré.", style: TextStyle( fontStyle: FontStyle.italic, color: Colors.grey[600]), ), ) else ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: workingPath.steps!.length, itemBuilder: (context, index) { final step = workingPath.steps![index]; return ListTile( leading: CircleAvatar(child: Text("${index + 1}")), title: HtmlWidget( step.title != null && step.title!.isNotEmpty ? step.title! .firstWhere( (t) => t.language == 'FR', orElse: () => step.title![0]) .value ?? "Étape $index" : "Étape $index", ), subtitle: isEscapeMode ? Text( "${step.quizQuestions?.length ?? 0} question(s)") : null, trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: Icon(Icons.edit, color: kPrimaryColor), onPressed: () { showNewOrUpdateGuidedStep( context, step, workingPath.id ?? "temp", isEscapeMode, (updatedStep) async { setState(() { workingPath.steps![index] = updatedStep; }); }, ); }, ), IconButton( icon: Icon(Icons.delete, color: kError), onPressed: () { setState(() { workingPath.steps!.removeAt(index); }); }, ), ], ), ); }, ), ], ), ), ), SizedBox(height: 16), // -- Boutons -- Row( mainAxisAlignment: MainAxisAlignment.end, children: [ SizedBox( height: 46, child: RoundedButton( text: "Annuler", press: () => Navigator.pop(context), color: kSecond, fontSize: 15, horizontal: 24, ), ), SizedBox(width: 12), SizedBox( height: 46, child: RoundedButton( text: isSaving ? "Sauvegarde..." : "Sauvegarder", icon: isSaving ? Icons.hourglass_empty : null, press: () async { if (isSaving) return; setState(() => isSaving = true); // Initialise les booleans null → false workingPath.isLinear ??= false; workingPath.requireSuccessToAdvance ??= false; workingPath.hideNextStepsUntilComplete ??= false; // Initialise les booleans nuls dans chaque étape for (final s in workingPath.steps ?? []) { s.isHiddenInitially ??= false; s.isStepTimer ??= false; s.isStepLocked ??= false; } try { await onSave(workingPath); if (context.mounted) Navigator.pop(context); } catch (e) { setState(() => isSaving = false); } }, color: kPrimaryColor, fontSize: 15, horizontal: 24, ), ), ], ), ], ), ), ); }, ); }, ); }