Thomas Fransolet 0054122829 Carte : filtre par categorie, editeur de point, et corrections users/quotas
Regroupe la configuration de carte en deux blocs (Carte / Points d'interet),
ajoute le filtre par categorie et l'editeur de point geographique, et passe
les ecrans Users et les jauges de quota par AppLocalizations (FR/EN/NL).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 16:22:39 +02:00

194 lines
6.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<String> 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<MenuConfig> {
late MenuDTO menuDTO;
List<SectionDTO> 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<String, dynamic> rawById = {};
bool isLoading = true;
@override
void initState() {
super.initState();
menuDTO = widget.initialValue;
WidgetsBinding.instance.addPostFrameCallback((_) => _loadFromApi());
}
SectionApi _api() {
final appContext = Provider.of<AppContext>(context, listen: false);
return (appContext.getContext() as ManagerAppContext).clientAPI!.sectionApi!;
}
Future<void> _loadFromApi() async {
if (menuDTO.id == null || !mounted) return;
try {
final rawList =
await _api().sectionGetAllSectionSubSections(menuDTO.id!);
if (!mounted) return;
final decoded = rawList == null
? <dynamic>[]
: jsonDecode(jsonEncode(rawList)) as List<dynamic>;
final sections = <SectionDTO>[];
final byId = <String, dynamic>{};
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<void> _onReorder(int oldIndex, int newIndex) async {
final l = AppLocalizations.of(context)!;
final previous = List<SectionDTO>.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<void> _createSubSection() async {
final l = AppLocalizations.of(context)!;
final appContext = Provider.of<AppContext>(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<void> _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<AppContext>(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,
);
}
}