import 'package:flutter/material.dart'; import 'package:manager_app/Components/common_loader.dart'; import 'package:manager_app/l10n/app_localizations.dart'; import 'package:manager_app/Components/message_notification.dart'; import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Screens/Configurations/listView_card_section.dart'; import 'package:manager_app/Screens/Configurations/new_section_popup.dart'; import 'package:manager_app/app_context.dart'; import 'package:manager_app/constants.dart'; import 'package:manager_api_new/api.dart'; import 'dart:convert'; import 'package:provider/provider.dart'; /// Les sous-sections d'un Menu, dans la grille de tuiles des sections. /// /// Ce sont des sections à part entière, avec leur propre type et leur propre /// écran de configuration : on continue donc de **naviguer** vers cet écran au /// clic, au lieu de les éditer en place. Seule la liste change — la liste /// horizontale enfermée dans `0,35 × hauteur` devient la même grille que les /// sections d'une configuration. class MenuConfig extends StatefulWidget { final String? color; final String? label; final MenuDTO initialValue; final ValueChanged onChanged; const MenuConfig({ Key? key, this.color, this.label, required this.initialValue, required this.onChanged, }) : super(key: key); @override _MenuConfigState createState() => _MenuConfigState(); } class _MenuConfigState extends State { late MenuDTO menuDTO; List subSections = []; /// L'écran de sous-section lit le JSON brut renvoyé par l'API, pas le DTO : /// on le garde indexé par identifiant, la grille étant triée par ordre. Map rawById = {}; bool isLoading = true; @override void initState() { super.initState(); menuDTO = widget.initialValue; WidgetsBinding.instance.addPostFrameCallback((_) => _loadFromApi()); } SectionApi _api() { final appContext = Provider.of(context, listen: false); return (appContext.getContext() as ManagerAppContext).clientAPI!.sectionApi!; } Future _loadFromApi() async { if (menuDTO.id == null || !mounted) return; try { final rawList = await _api().sectionGetAllSectionSubSections(menuDTO.id!); if (!mounted) return; final decoded = rawList == null ? [] : jsonDecode(jsonEncode(rawList)) as List; final sections = []; final byId = {}; for (var i = 0; i < decoded.length; i++) { final section = SectionDTO.fromJson(decoded[i]); if (section == null) continue; sections.add(section); if (section.id != null) byId[section.id!] = rawList![i]; } sections.sort((a, b) => (a.order ?? 0).compareTo(b.order ?? 0)); setState(() { rawById = byId; subSections = sections; menuDTO.sections = sections; isLoading = false; }); } catch (e) { if (mounted) setState(() => isLoading = false); } } /// ⚠️ La liste entière est renumérotée : l'ancien code n'écrivait que /// l'`order` de la sous-section déplacée, ce qui laissait des rangs en double. Future _onReorder(int oldIndex, int newIndex) async { final l = AppLocalizations.of(context)!; final previous = List.from(subSections); setState(() { final item = subSections.removeAt(oldIndex); subSections.insert(newIndex, item); for (var i = 0; i < subSections.length; i++) { subSections[i].order = i; } }); try { await Future.wait(subSections.map((s) => _api().sectionUpdate(s))); showNotification( kSuccess, kWhite, l.subSectionOrderUpdatedSuccess, context, null); menuDTO.sections = subSections; widget.onChanged(jsonEncode(menuDTO).toString()); } catch (e) { setState(() { subSections = previous; for (var i = 0; i < subSections.length; i++) { subSections[i].order = i; } }); showNotification( kError, kWhite, l.subSectionOrderUpdateError, context, null); } } Future _createSubSection() async { final l = AppLocalizations.of(context)!; final appContext = Provider.of(context, listen: false); final managerContext = appContext.getContext() as ManagerAppContext; final newSubSection = await showNewSection( managerContext.selectedConfiguration!.id!, appContext, context, true); if (newSubSection == null) return; try { newSubSection.instanceId = managerContext.instanceId; newSubSection.isBeacon = false; newSubSection.isActive = true; newSubSection.order = subSections.length; await _api().sectionCreate(newSubSection); showNotification( kSuccess, kWhite, l.subSectionCreatedSuccess, context, null); await _loadFromApi(); } catch (e) { showNotification(kError, kWhite, l.subSectionCreateError, context, null); } } Future _toggleVisibility(SectionDTO section) async { final l = AppLocalizations.of(context)!; final newValue = section.isActive == false; setState(() => section.isActive = newValue); try { await _api().sectionSetVisibility(section.id!, newValue); showNotification( kSuccess, kWhite, newValue ? l.sectionShownSuccess : l.sectionHiddenSuccess, context, null); } catch (e) { setState(() => section.isActive = !newValue); showNotification(kError, kWhite, l.sectionVisibilityError, context, null); } } /// Ouvre l'écran de la sous-section. Il lit `selectedSubSectionRawData`, donc /// on lui passe le JSON brut correspondant, pas seulement le DTO. void _openSubSection(SectionDTO section, int index) { final appContext = Provider.of(context, listen: false); final managerContext = appContext.getContext() as ManagerAppContext; managerContext.selectedSubSection = section; managerContext.selectedSubSectionRawData = rawById[section.id]; appContext.setContext(managerContext); } @override Widget build(BuildContext context) { if (isLoading) { return const Padding( padding: EdgeInsets.all(kSpace8), child: Center(child: CommonLoader()), ); } return SectionGrid( sections: subSections, onReorder: _onReorder, onAdd: _createSubSection, onTap: _openSubSection, onToggleVisibility: _toggleVisibility, ); } }