Wip changements fantastiquement rapide d'antigravity, très bonne base mais du wip

This commit is contained in:
Thomas Fransolet 2026-03-02 09:06:53 +01:00
parent 9e06afb29e
commit ded4620bf2
22 changed files with 3839 additions and 1134 deletions

View File

@ -0,0 +1,144 @@
import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/Components/map_geometry_picker.dart';
import 'package:manager_app/constants.dart';
class GeometryInputContainer extends StatelessWidget {
final String label;
final GeometryDTO? initialGeometry;
final String? initialColor;
final Function(GeometryDTO, String) onSave;
final Color color;
final bool isSmall;
GeometryInputContainer({
required this.label,
this.initialGeometry,
this.initialColor,
required this.onSave,
this.color = kPrimaryColor,
this.isSmall = false,
});
@override
Widget build(BuildContext context) {
String typeInfo = initialGeometry?.type ?? "Aucun";
int pointCount = 0;
if (initialGeometry?.coordinates != null) {
if (initialGeometry!.type == "Point") {
pointCount = 1;
} else if (initialGeometry!.coordinates is List) {
pointCount = (initialGeometry!.coordinates as List).length;
}
}
Size size = MediaQuery.of(context).size;
return Container(
margin: EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Container(
width: 150,
child: Text(
label,
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 16,
),
),
),
Container(
width: isSmall ? size.width * 0.15 : size.width * 0.25,
child: InkWell(
onTap: () {
showDialog(
context: context,
builder: (context) => MapGeometryPicker(
initialGeometry: initialGeometry,
initialColor: initialColor,
onSave: onSave,
),
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(29),
border: Border.all(color: kSecond),
),
child: Row(
children: [
Icon(
_getIconForType(initialGeometry?.type),
color: color,
size: 20,
),
SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
initialGeometry != null
? "$typeInfo ($pointCount pts)"
: "Définir",
style: TextStyle(
fontSize: 13,
color: initialGeometry != null ? kBlack : kSecond,
overflow: TextOverflow.ellipsis,
),
),
if (initialColor != null)
Row(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: _parseColor(initialColor!),
shape: BoxShape.circle,
),
),
SizedBox(width: 4),
Text(initialColor!,
style: TextStyle(
fontSize: 9, color: Colors.grey)),
],
),
],
),
),
Icon(Icons.edit, color: kSecond, size: 16),
],
),
),
),
),
],
),
);
}
IconData _getIconForType(String? type) {
switch (type) {
case "Point":
return Icons.location_on;
case "LineString":
return Icons.show_chart;
case "Polygon":
return Icons.pentagon;
default:
return Icons.map;
}
}
Color _parseColor(String hex) {
try {
return Color(int.parse(hex.replaceFirst('#', '0xFF')));
} catch (e) {
return kPrimaryColor;
}
}
}

View File

@ -0,0 +1,361 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.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/color_picker.dart';
class MapGeometryPicker extends StatefulWidget {
final GeometryDTO? initialGeometry;
final String? initialColor;
final Function(GeometryDTO, String) onSave;
MapGeometryPicker({
this.initialGeometry,
this.initialColor,
required this.onSave,
});
@override
_MapGeometryPickerState createState() => _MapGeometryPickerState();
}
class _MapGeometryPickerState extends State<MapGeometryPicker> {
List<LatLng> points = [];
String currentType = "Point";
Color selectedColor = kPrimaryColor;
final MapController _mapController = MapController();
@override
void initState() {
super.initState();
if (widget.initialGeometry != null) {
currentType = widget.initialGeometry!.type ?? "Point";
_parseInitialGeometry();
}
if (widget.initialColor != null) {
try {
selectedColor = _hexToColor(widget.initialColor!);
} catch (e) {
selectedColor = kPrimaryColor;
}
}
}
Color _hexToColor(String hex) {
hex = hex.replaceFirst('#', '');
if (hex.length == 6) hex = 'FF' + hex;
return Color(int.parse(hex, radix: 16));
}
void _parseInitialGeometry() {
if (widget.initialGeometry?.coordinates == null) return;
try {
if (currentType == "Point") {
var coords = widget.initialGeometry!.coordinates as List<dynamic>;
points = [LatLng(coords[0].toDouble(), coords[1].toDouble())];
} else if (currentType == "LineString" || currentType == "Polygon") {
var list = widget.initialGeometry!.coordinates as List<dynamic>;
points = list.map((e) {
var pair = e as List<dynamic>;
return LatLng(pair[0].toDouble(), pair[1].toDouble());
}).toList();
}
} catch (e) {
print("Error parsing geometry: $e");
}
}
void _handleTap(TapPosition tapPosition, LatLng latLng) {
setState(() {
if (currentType == "Point") {
points = [latLng];
} else {
points.add(latLng);
}
});
}
GeometryDTO _buildGeometry() {
if (currentType == "Point") {
return GeometryDTO(
type: "Point",
coordinates: points.isNotEmpty
? [points[0].latitude, points[0].longitude]
: null,
);
} else {
return GeometryDTO(
type: currentType,
coordinates: points.map((e) => [e.latitude, e.longitude]).toList(),
);
}
}
String _colorToHex(Color color) {
return '#${color.value.toRadixString(16).padLeft(8, '0').substring(2)}';
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Dialog(
insetPadding: EdgeInsets.all(20),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
child: Container(
width: size.width * 0.9,
height: size.height * 0.9,
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
color: kPrimaryColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Éditeur de Géométrie",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold)),
Row(
children: [
IconButton(
icon: Icon(Icons.palette, color: Colors.white),
onPressed: () {
showColorPicker(selectedColor, (Color color) {
setState(() => selectedColor = color);
}, context);
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.white),
onPressed: () => setState(() => points.clear()),
),
IconButton(
icon: Icon(Icons.close, color: Colors.white),
onPressed: () => Navigator.pop(context),
),
],
),
],
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
Wrap(
spacing: 12,
alignment: WrapAlignment.center,
children: [
_buildTypeButton("Point", Icons.location_on),
_buildTypeButton("LineString", Icons.show_chart),
_buildTypeButton("Polygon", Icons.pentagon),
],
),
SizedBox(height: 8),
Text(
_getInstructions(),
style: TextStyle(
fontStyle: FontStyle.italic, color: Colors.grey[600]),
textAlign: TextAlign.center,
),
],
),
),
Expanded(
child: Stack(
children: [
FlutterMap(
key: _mapKey,
mapController: _mapController,
options: MapOptions(
initialCenter: points.isNotEmpty
? points[0]
: LatLng(50.429333, 4.891434),
initialZoom: 14,
onTap: _handleTap,
),
children: [
TileLayer(
urlTemplate:
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
),
if (currentType == "Polygon" && points.length >= 3)
PolygonLayer(
polygons: [
Polygon(
points: points,
color: selectedColor.withOpacity(0.3),
borderStrokeWidth: 2,
borderColor: selectedColor,
isFilled: true,
),
],
),
if (currentType == "LineString" && points.length >= 2)
PolylineLayer(
polylines: [
Polyline(
points: points,
color: selectedColor,
strokeWidth: 4,
),
],
),
MarkerLayer(
markers: points.asMap().entries.map((entry) {
int idx = entry.key;
LatLng p = entry.value;
return Marker(
point: p,
width: 30,
height: 30,
child: GestureDetector(
onPanUpdate: (details) {
_handleDrag(idx, details);
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
border: Border.all(
color: selectedColor, width: 2),
boxShadow: [
BoxShadow(
blurRadius: 4,
color: Colors.black26,
offset: Offset(0, 2))
],
),
child: Center(
child: Icon(Icons.circle,
color: selectedColor, size: 14),
),
),
),
);
}).toList(),
),
],
),
],
),
),
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, -2))
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
height: 45,
child: RoundedButton(
text: "Annuler",
color: Colors.grey[200]!,
textColor: Colors.black87,
press: () => Navigator.pop(context),
fontSize: 16,
horizontal: 24,
),
),
SizedBox(width: 12),
Container(
height: 45,
child: RoundedButton(
text: "Sauvegarder",
color: kPrimaryColor,
press: () {
widget.onSave(
_buildGeometry(), _colorToHex(selectedColor));
Navigator.pop(context);
},
fontSize: 16,
horizontal: 32,
),
),
],
),
)
],
),
),
),
);
}
String _getInstructions() {
switch (currentType) {
case "Point":
return "Touchez la carte pour placer le point. Faites glisser le point pour le déplacer.";
case "LineString":
return "Touchez la carte pour ajouter des points à la ligne. Faites glisser un point pour le déplacer.";
case "Polygon":
return "Touchez la carte pour définir les sommets du polygone. Faites glisser un sommet pour le déplacer.";
default:
return "";
}
}
final GlobalKey _mapKey = GlobalKey();
void _handleDrag(int index, DragUpdateDetails details) {
if (index < 0 || index >= points.length) return;
final RenderBox? mapBox =
_mapKey.currentContext?.findRenderObject() as RenderBox?;
if (mapBox != null) {
final Offset localOffset = mapBox.globalToLocal(details.globalPosition);
try {
LatLng newLatLng = _mapController.camera
.pointToLatLng(math.Point(localOffset.dx, localOffset.dy));
setState(() {
points[index] = newLatLng;
});
} catch (e) {
print("Error dragging point: $e");
}
}
}
Widget _buildTypeButton(String type, IconData icon) {
bool isSelected = currentType == type;
return ChoiceChip(
label: Text(type == "LineString"
? "Ligne"
: type == "Polygon"
? "Polygone"
: "Point"),
avatar: Icon(icon,
size: 18, color: isSelected ? Colors.white : kPrimaryColor),
selected: isSelected,
onSelected: (val) {
if (val) {
setState(() {
currentType = type;
if (currentType == "Point" && points.length > 1) {
points = [points[0]];
}
});
}
},
selectedColor: kPrimaryColor,
labelStyle: TextStyle(color: isSelected ? Colors.white : kPrimaryColor),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
);
}
}

View File

@ -11,8 +11,8 @@ class RoundedButton extends StatelessWidget {
final double? vertical; final double? vertical;
final double? horizontal; final double? horizontal;
const RoundedButton({ const RoundedButton(
Key? key, {Key? key,
required this.text, required this.text,
required this.press, required this.press,
this.icon, this.icon,
@ -20,28 +20,24 @@ class RoundedButton extends StatelessWidget {
this.textColor = kWhite, this.textColor = kWhite,
required this.fontSize, required this.fontSize,
this.vertical, this.vertical,
this.horizontal this.horizontal})
}) : super(key: key); : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
//Size size = MediaQuery.of(context).size;
return TextButton( return TextButton(
style: ButtonStyle( style: ButtonStyle(
padding: MaterialStateProperty.resolveWith((states) => EdgeInsets.symmetric(vertical: this.vertical != null ? this.vertical! : 25, horizontal: this.horizontal != null ? this.horizontal!: (icon == null ? 85 : 30))), padding: MaterialStateProperty.all(EdgeInsets.symmetric(
backgroundColor: MaterialStateColor.resolveWith((states) => color), vertical: vertical ?? 12,
horizontal: horizontal ?? (icon == null ? 20 : 15),
)),
backgroundColor: MaterialStateProperty.all(color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>( shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder( RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0), borderRadius: BorderRadius.circular(30.0),
) ))),
) onPressed: () => {press()},
), child: getValue(icon));
onPressed: () => {
press()
},
child: getValue(icon)
);
} }
getValue(icon) { getValue(icon) {
@ -52,15 +48,16 @@ class RoundedButton extends StatelessWidget {
Center( Center(
child: AutoSizeText( child: AutoSizeText(
text, text,
style: new TextStyle(color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400), style: new TextStyle(
color: textColor,
fontSize: fontSize,
fontWeight: FontWeight.w400),
maxLines: 2, maxLines: 2,
maxFontSize: fontSize, maxFontSize: fontSize,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
SizedBox( SizedBox(width: 10),
width: 10
),
Icon( Icon(
icon, icon,
color: textColor, color: textColor,
@ -71,7 +68,8 @@ class RoundedButton extends StatelessWidget {
} else { } else {
return AutoSizeText( return AutoSizeText(
text, text,
style: new TextStyle(color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400), style: new TextStyle(
color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400),
maxLines: 2, maxLines: 2,
textAlign: TextAlign.center, textAlign: TextAlign.center,
); );

View File

@ -1,19 +1,17 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/Components/check_input_container.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/multi_string_input_container.dart';
import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/Components/single_select_container.dart'; import 'package:manager_app/Components/single_select_container.dart';
import 'dart:convert'; import 'showNewOrUpdateEventAgenda.dart';
import 'package:manager_app/constants.dart';
class AgendaConfig extends StatefulWidget { class AgendaConfig extends StatefulWidget {
final String? color; final String? color;
final String? label; final String? label;
final AgendaDTO initialValue; final AgendaDTO initialValue;
final ValueChanged<AgendaDTO> onChanged; final ValueChanged<AgendaDTO> onChanged;
const AgendaConfig({ const AgendaConfig({
Key? key, Key? key,
this.color, this.color,
@ -31,14 +29,13 @@ class _AgendaConfigState extends State<AgendaConfig> {
@override @override
void initState() { void initState() {
AgendaDTO test = widget.initialValue;
agendaDTO = test;
super.initState(); super.initState();
agendaDTO = widget.initialValue;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; //Size size = MediaQuery.of(context).size;
var mapProviderIn = ""; var mapProviderIn = "";
switch (agendaDTO.agendaMapProvider) { switch (agendaDTO.agendaMapProvider) {
@ -53,17 +50,16 @@ class _AgendaConfigState extends State<AgendaConfig> {
break; break;
} }
return SingleChildScrollView( return Column(
child: Padding( children: [
Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Container(
height: size.height * 0.3,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
CheckInputContainer( CheckInputContainer(
label: "En ligne :", label: "En ligne :",
isChecked: agendaDTO.isOnlineAgenda!, isChecked: agendaDTO.isOnlineAgenda ?? true,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
agendaDTO.isOnlineAgenda = value; agendaDTO.isOnlineAgenda = value;
@ -75,8 +71,9 @@ class _AgendaConfigState extends State<AgendaConfig> {
label: "Service carte :", label: "Service carte :",
color: Colors.black, color: Colors.black,
initialValue: mapProviderIn, initialValue: mapProviderIn,
inputValues: map_providers, inputValues: ["Google", "MapBox"],
onChanged: (String value) { onChanged: (String value) {
setState(() {
switch (value) { switch (value) {
case "Google": case "Google":
agendaDTO.agendaMapProvider = MapProvider.Google; agendaDTO.agendaMapProvider = MapProvider.Google;
@ -86,22 +83,21 @@ class _AgendaConfigState extends State<AgendaConfig> {
break; break;
} }
widget.onChanged(agendaDTO); widget.onChanged(agendaDTO);
} });
},
), ),
if (agendaDTO.isOnlineAgenda == true)
MultiStringInputContainer( MultiStringInputContainer(
label: "Fichiers json :", label: "Fichiers json :",
resourceTypes: [ResourceType.Json, ResourceType.JsonUrl], resourceTypes: [ResourceType.Json, ResourceType.JsonUrl],
modalLabel: "JSON", modalLabel: "JSON",
color: kPrimaryColor, color: kPrimaryColor,
initialValue: agendaDTO.resourceIds!, initialValue: agendaDTO.resourceIds ?? [],
isTitle: false, isTitle: false,
onGetResult: (value) { onGetResult: (value) {
setState(() { setState(() {
if (agendaDTO.resourceIds != value) {
agendaDTO.resourceIds = value; agendaDTO.resourceIds = value;
//save(true, articleDTO, appContext);
widget.onChanged(agendaDTO); widget.onChanged(agendaDTO);
}
}); });
}, },
maxLines: 1, maxLines: 1,
@ -109,17 +105,99 @@ class _AgendaConfigState extends State<AgendaConfig> {
], ],
), ),
), ),
if (agendaDTO.isOnlineAgenda == false) ...[
Divider(),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Évènements Manuels",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
ElevatedButton.icon(
icon: Icon(Icons.add),
label: Text("Ajouter un évènement"),
onPressed: () {
showNewOrUpdateEventAgenda(
context,
null,
agendaDTO.id ?? "temp",
(newEvent) {
setState(() {
agendaDTO.events = [
...(agendaDTO.events ?? []),
newEvent
];
widget.onChanged(agendaDTO);
});
},
);
},
style: ElevatedButton.styleFrom(
backgroundColor: kSuccess, foregroundColor: kWhite),
),
],
),
),
Expanded(
child: (agendaDTO.events == null || agendaDTO.events!.isEmpty)
? Center(
child: Text("Aucun évènement manuel",
style: TextStyle(fontStyle: FontStyle.italic)))
: ListView.builder(
itemCount: agendaDTO.events!.length,
itemBuilder: (context, index) {
final event = agendaDTO.events![index];
return Card(
margin:
EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
title: Text(event.label
?.firstWhere((t) => t.language == 'FR',
orElse: () => event.label![0])
.value ??
"Évènement $index"),
subtitle:
Text(event.address?.address ?? "Pas d'adresse"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit, color: kPrimaryColor),
onPressed: () {
showNewOrUpdateEventAgenda(
context,
event,
agendaDTO.id ?? "temp",
(updatedEvent) {
setState(() {
agendaDTO.events![index] = updatedEvent;
widget.onChanged(agendaDTO);
});
},
);
},
),
IconButton(
icon: Icon(Icons.delete, color: kError),
onPressed: () {
setState(() {
agendaDTO.events!.removeAt(index);
widget.onChanged(agendaDTO);
});
},
),
],
),
), ),
); );
/*return ResourceInputContainer( },
label: "Fichier JSON :", ),
initialValue: agendaDTO.resourceId == null ? '': agendaDTO.resourceId, ),
inResourceTypes: [ResourceType.Json, ResourceType.JsonUrl], ],
onChanged: (ResourceDTO resourceDTO) { ],
agendaDTO.resourceUrl = resourceDTO.url; );
agendaDTO.resourceId = resourceDTO.id;
widget.onChanged(jsonEncode(agendaDTO).toString());
}
);*/
} }
} }

View File

@ -0,0 +1,208 @@
import 'package:flutter/material.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/string_input_container.dart';
import 'package:manager_app/Components/geometry_input_container.dart';
void showNewOrUpdateEventAgenda(
BuildContext context,
EventAgendaDTO? event,
String agendaId,
Function(EventAgendaDTO) onSave,
) {
EventAgendaDTO workingEvent = event != null
? EventAgendaDTO.fromJson(event.toJson())!
: EventAgendaDTO(
label: [],
description: [],
sectionAgendaId: agendaId,
address: EventAgendaDTOAddress(
address: "",
city: "",
country: "",
),
);
showDialog(
context: context,
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.78;
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: [
Text(
event == null ? "Nouvel Évènement" : "Modifier l'Évènement",
style: TextStyle(
color: kPrimaryColor,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(height: 16),
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 de l'évènement",
initialValue: workingEvent.label ?? [],
onGetResult: (val) =>
setState(() => workingEvent.label = val),
maxLines: 1,
isTitle: true,
),
),
SizedBox(width: 20),
SizedBox(
width: halfWidth,
child: MultiStringInputContainer(
label: "Description :",
modalLabel: "Description de l'évènement",
initialValue: workingEvent.description ?? [],
onGetResult: (val) => setState(
() => workingEvent.description = val),
maxLines: 5,
isTitle: false,
),
),
],
),
Divider(height: 24),
// Site | Tel | Email
Row(
children: [
SizedBox(
width: thirdWidth,
child: StringInputContainer(
label: "Site Web :",
initialValue: workingEvent.website ?? "",
onChanged: (val) => setState(
() => workingEvent.website = val),
),
),
SizedBox(width: 20),
SizedBox(
width: thirdWidth,
child: StringInputContainer(
label: "Téléphone :",
initialValue: workingEvent.phone ?? "",
onChanged: (val) =>
setState(() => workingEvent.phone = val),
),
),
SizedBox(width: 20),
SizedBox(
width: thirdWidth,
child: StringInputContainer(
label: "Email :",
initialValue: workingEvent.email ?? "",
onChanged: (val) =>
setState(() => workingEvent.email = val),
),
),
],
),
Divider(height: 24),
Text("Localisation / Géométrie",
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 8),
StringInputContainer(
label: "Adresse (Texte) :",
initialValue: workingEvent.address?.address ?? "",
onChanged: (val) {
setState(() {
if (workingEvent.address == null)
workingEvent.address =
EventAgendaDTOAddress();
workingEvent.address!.address = val;
});
},
),
SizedBox(height: 8),
GeometryInputContainer(
label: "Emplacement sur la carte :",
initialGeometry: workingEvent.address?.geometry !=
null
? GeometryDTO.fromJson(
workingEvent.address!.geometry!.toJson())
: null,
initialColor: workingEvent.address?.polyColor,
onSave: (geo, color) {
setState(() {
if (workingEvent.address == null)
workingEvent.address =
EventAgendaDTOAddress();
workingEvent.address!.geometry =
EventAddressDTOGeometry.fromJson(
geo.toJson());
workingEvent.address!.polyColor = color;
});
},
),
],
),
),
),
SizedBox(height: 16),
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: "Sauvegarder",
press: () {
onSave(workingEvent);
Navigator.pop(context);
},
color: kPrimaryColor,
fontSize: 15,
horizontal: 24,
),
),
],
),
],
),
),
);
},
);
},
);
}

View File

@ -0,0 +1,253 @@
import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/constants.dart';
import 'package:intl/intl.dart';
import 'showNewOrUpdateProgrammeBlock.dart';
class EventConfig extends StatefulWidget {
final SectionEventDTO initialValue;
final ValueChanged<SectionEventDTO> onChanged;
const EventConfig({
Key? key,
required this.initialValue,
required this.onChanged,
}) : super(key: key);
@override
_EventConfigState createState() => _EventConfigState();
}
class _EventConfigState extends State<EventConfig> {
late SectionEventDTO eventDTO;
@override
void initState() {
super.initState();
eventDTO = widget.initialValue;
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: ListTile(
title: Text("Date de début"),
subtitle: Text(eventDTO.startDate != null
? DateFormat('dd/MM/yyyy HH:mm')
.format(eventDTO.startDate!)
: "Non définie"),
trailing: Icon(Icons.calendar_today),
onTap: () async {
DateTime initialDate = eventDTO.startDate ?? DateTime.now();
if (initialDate.isBefore(DateTime(2000))) {
initialDate = DateTime.now();
}
DateTime? picked = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: kPrimaryColor,
onPrimary: kWhite,
onSurface: kSecond,
),
),
child: child!,
);
},
);
if (picked != null) {
TimeOfDay? time = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(
eventDTO.startDate ?? DateTime.now()),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: kPrimaryColor,
onPrimary: kWhite,
onSurface: kSecond,
),
),
child: child!,
);
},
);
if (time != null) {
setState(() {
eventDTO.startDate = DateTime(picked.year,
picked.month, picked.day, time.hour, time.minute);
widget.onChanged(eventDTO);
});
}
}
},
),
),
Expanded(
child: ListTile(
title: Text("Date de fin"),
subtitle: Text(eventDTO.endDate != null
? DateFormat('dd/MM/yyyy HH:mm').format(eventDTO.endDate!)
: "Non définie"),
trailing: Icon(Icons.calendar_today),
onTap: () async {
DateTime initialDate = eventDTO.endDate ??
DateTime.now().add(Duration(days: 1));
if (initialDate.isBefore(DateTime(2000))) {
initialDate = DateTime.now().add(Duration(days: 1));
}
DateTime? picked = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: kPrimaryColor,
onPrimary: kWhite,
onSurface: kSecond,
),
),
child: child!,
);
},
);
if (picked != null) {
TimeOfDay? time = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(eventDTO.endDate ??
DateTime.now().add(Duration(days: 1))),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: kPrimaryColor,
onPrimary: kWhite,
onSurface: kSecond,
),
),
child: child!,
);
},
);
if (time != null) {
setState(() {
eventDTO.endDate = DateTime(picked.year, picked.month,
picked.day, time.hour, time.minute);
widget.onChanged(eventDTO);
});
}
}
},
),
),
],
),
),
Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Programme",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
ElevatedButton.icon(
icon: Icon(Icons.add),
label: Text("Ajouter un bloc"),
onPressed: () {
showNewOrUpdateProgrammeBlock(
context,
null,
(newBlock) {
setState(() {
eventDTO.programme = [
...(eventDTO.programme ?? []),
newBlock
];
widget.onChanged(eventDTO);
});
},
);
},
style: ElevatedButton.styleFrom(
backgroundColor: kSuccess, foregroundColor: kWhite),
),
],
),
),
Expanded(
child: (eventDTO.programme == null || eventDTO.programme!.isEmpty)
? Center(
child: Text("Aucun bloc de programme défini",
style: TextStyle(fontStyle: FontStyle.italic)))
: ListView.builder(
itemCount: eventDTO.programme!.length,
itemBuilder: (context, index) {
final block = eventDTO.programme![index];
return Card(
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
title: Text(
block.title != null && block.title!.isNotEmpty
? block.title!
.firstWhere((t) => t.language == 'FR',
orElse: () => block.title![0])
.value ??
"Bloc ${index + 1}"
: "Bloc ${index + 1}"),
subtitle: Text(
"${block.startTime != null ? DateFormat('HH:mm').format(block.startTime!) : '??'} - ${block.endTime != null ? DateFormat('HH:mm').format(block.endTime!) : '??'}"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit, color: kPrimaryColor),
onPressed: () {
showNewOrUpdateProgrammeBlock(
context,
block,
(updatedBlock) {
setState(() {
eventDTO.programme![index] = updatedBlock;
widget.onChanged(eventDTO);
});
},
);
},
),
IconButton(
icon: Icon(Icons.delete, color: kError),
onPressed: () {
setState(() {
eventDTO.programme!.removeAt(index);
widget.onChanged(eventDTO);
});
},
),
],
),
),
);
},
),
),
],
);
}
}

View File

@ -0,0 +1,235 @@
import 'package:flutter/material.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:intl/intl.dart';
void showNewOrUpdateProgrammeBlock(
BuildContext context,
ProgrammeBlock? block,
Function(ProgrammeBlock) onSave,
) {
ProgrammeBlock workingBlock = block != null
? ProgrammeBlock.fromJson(block.toJson())!
: ProgrammeBlock(
title: [],
description: [],
startTime: DateTime.now(),
endTime: DateTime.now().add(Duration(hours: 1)),
);
showDialog(
context: context,
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.7;
final double contentWidth = dialogWidth - 48;
final double halfWidth = (contentWidth - 20) / 2;
return Dialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
width: dialogWidth,
constraints: BoxConstraints(maxHeight: screenHeight * 0.85),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
block == null
? "Nouveau Bloc de Programme"
: "Modifier le Bloc",
style: TextStyle(
color: kPrimaryColor,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(height: 16),
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 bloc",
initialValue: workingBlock.title ?? [],
onGetResult: (val) =>
setState(() => workingBlock.title = val),
maxLines: 1,
isTitle: true,
),
),
SizedBox(width: 20),
SizedBox(
width: halfWidth,
child: MultiStringInputContainer(
label: "Description :",
modalLabel: "Description du bloc",
initialValue: workingBlock.description ?? [],
onGetResult: (val) => setState(
() => workingBlock.description = val),
maxLines: 1,
isTitle: false,
),
),
],
),
Divider(height: 24),
// Heures côte à côte
Row(
children: [
SizedBox(
width: halfWidth,
child: ListTile(
leading: Icon(Icons.schedule,
color: kPrimaryColor),
title: Text("Heure de début"),
subtitle: Text(
workingBlock.startTime != null
? DateFormat('HH:mm')
.format(workingBlock.startTime!)
: "Non définie",
style: TextStyle(
color: kPrimaryColor,
fontWeight: FontWeight.bold),
),
onTap: () async {
TimeOfDay? time = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(
workingBlock.startTime ??
DateTime.now()),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: kPrimaryColor,
onPrimary: kWhite,
onSurface: kSecond,
),
),
child: child!,
);
},
);
if (time != null) {
setState(() {
DateTime now = DateTime.now();
workingBlock.startTime = DateTime(
now.year,
now.month,
now.day,
time.hour,
time.minute);
});
}
},
),
),
SizedBox(width: 20),
SizedBox(
width: halfWidth,
child: ListTile(
leading: Icon(Icons.schedule_outlined,
color: kPrimaryColor),
title: Text("Heure de fin"),
subtitle: Text(
workingBlock.endTime != null
? DateFormat('HH:mm')
.format(workingBlock.endTime!)
: "Non définie",
style: TextStyle(
color: kPrimaryColor,
fontWeight: FontWeight.bold),
),
onTap: () async {
TimeOfDay? time = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(
workingBlock.endTime ??
DateTime.now()
.add(Duration(hours: 1))),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: kPrimaryColor,
onPrimary: kWhite,
onSurface: kSecond,
),
),
child: child!,
);
},
);
if (time != null) {
setState(() {
DateTime now = DateTime.now();
workingBlock.endTime = DateTime(
now.year,
now.month,
now.day,
time.hour,
time.minute);
});
}
},
),
),
],
),
],
),
),
),
SizedBox(height: 16),
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: "Sauvegarder",
press: () {
onSave(workingBlock);
Navigator.pop(context);
},
color: kPrimaryColor,
fontSize: 15,
horizontal: 24,
),
),
],
),
],
),
),
);
},
);
},
);
}

View File

@ -4,8 +4,7 @@ import 'package:manager_app/Components/multi_string_input_and_resource_container
import 'package:manager_app/Components/number_input_container.dart'; import 'package:manager_app/Components/number_input_container.dart';
import 'package:manager_app/Components/resource_input_container.dart'; import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
import 'dart:convert'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Parcours/parcours_config.dart';
import 'package:manager_app/constants.dart'; import 'package:manager_app/constants.dart';
class GameConfig extends StatefulWidget { class GameConfig extends StatefulWidget {
@ -30,33 +29,73 @@ class _GameConfigState extends State<GameConfig> {
@override @override
void initState() { void initState() {
GameDTO test = widget.initialValue; gameDTO = widget.initialValue;
/*if(test.puzzleImage == null) { gameDTO.rows = gameDTO.rows ?? 3;
test.puzzleImage = ResourceDTO(); gameDTO.cols = gameDTO.cols ?? 3;
}*/ gameDTO.gameType = gameDTO.gameType ?? GameTypes.number0;
gameDTO = test;
gameDTO.rows = gameDTO.rows == null ? 3 : gameDTO.rows;
gameDTO.cols = gameDTO.cols == null ? 3 : gameDTO.cols;
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return Column(
child: Column( children: [
mainAxisAlignment: MainAxisAlignment.spaceEvenly, Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Type de Jeu : ",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
DropdownButton<GameTypes>(
value: gameDTO.gameType,
items: [
DropdownMenuItem(
value: GameTypes.number0, child: Text("Puzzle")),
DropdownMenuItem(
value: GameTypes.number1, child: Text("Puzzle Glissant")),
DropdownMenuItem(
value: GameTypes.number2, child: Text("Escape Game")),
],
onChanged: (val) {
setState(() {
gameDTO.gameType = val;
widget.onChanged(gameDTO);
});
},
),
],
),
),
if (gameDTO.gameType == GameTypes.number2) ...[
// Escape Mode: Parcours Config
Expanded(
child: ParcoursConfig(
initialValue: gameDTO.guidedPaths ?? [],
parentId: gameDTO.id!,
isEvent: false,
isEscapeMode: true,
onChanged: (paths) {
setState(() {
gameDTO.guidedPaths = paths;
widget.onChanged(gameDTO);
});
},
),
),
] else ...[
// Regular Puzzle Mode
Column(
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
ResourceInputContainer( ResourceInputContainer(
label: "Image du puzzle :", label: "Image du puzzle :",
initialValue: gameDTO.puzzleImageId == null ? '': gameDTO.puzzleImageId, initialValue: gameDTO.puzzleImageId ?? '',
onChanged: (ResourceDTO resourceDTO) { onChanged: (ResourceDTO resourceDTO) {
setState(() { setState(() {
if(resourceDTO.id == null) if (resourceDTO.id == null) {
{
gameDTO.puzzleImageId = null; gameDTO.puzzleImageId = null;
gameDTO.puzzleImage = null; gameDTO.puzzleImage = null;
} else { } else {
@ -65,7 +104,7 @@ class _GameConfigState extends State<GameConfig> {
} }
widget.onChanged(gameDTO); widget.onChanged(gameDTO);
}); });
} },
), ),
Container( Container(
height: 100, height: 100,
@ -74,19 +113,23 @@ class _GameConfigState extends State<GameConfig> {
modalLabel: "Message départ", modalLabel: "Message départ",
fontSize: 20, fontSize: 20,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: gameDTO.messageDebut != null ? gameDTO.messageDebut! : [], initialValue: gameDTO.messageDebut ?? [],
resourceTypes: [ResourceType.Image, ResourceType.ImageUrl, ResourceType.VideoUrl, ResourceType.Video, ResourceType.Audio], resourceTypes: [
ResourceType.Image,
ResourceType.ImageUrl,
ResourceType.VideoUrl,
ResourceType.Video,
ResourceType.Audio
],
onGetResult: (value) { onGetResult: (value) {
if (gameDTO.messageDebut != value) {
setState(() { setState(() {
gameDTO.messageDebut = value; gameDTO.messageDebut = value;
widget.onChanged(gameDTO); widget.onChanged(gameDTO);
}); });
}
}, },
maxLines: 1, maxLines: 1,
isTitle: false isTitle: false,
) ),
), ),
Container( Container(
height: 100, height: 100,
@ -95,19 +138,23 @@ class _GameConfigState extends State<GameConfig> {
modalLabel: "Message fin", modalLabel: "Message fin",
fontSize: 20, fontSize: 20,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: gameDTO.messageFin != null ? gameDTO.messageFin! : [], initialValue: gameDTO.messageFin ?? [],
resourceTypes: [ResourceType.Image, ResourceType.ImageUrl, ResourceType.VideoUrl, ResourceType.Video, ResourceType.Audio], resourceTypes: [
ResourceType.Image,
ResourceType.ImageUrl,
ResourceType.VideoUrl,
ResourceType.Video,
ResourceType.Audio
],
onGetResult: (value) { onGetResult: (value) {
if (gameDTO.messageFin != value) {
setState(() { setState(() {
gameDTO.messageFin = value; gameDTO.messageFin = value;
widget.onChanged(gameDTO); widget.onChanged(gameDTO);
}); });
}
}, },
maxLines: 1, maxLines: 1,
isTitle: false isTitle: false,
) ),
), ),
], ],
), ),
@ -118,7 +165,7 @@ class _GameConfigState extends State<GameConfig> {
height: 100, height: 100,
child: NumberInputContainer( child: NumberInputContainer(
label: "Nombre de lignes :", label: "Nombre de lignes :",
initialValue: gameDTO.rows!, initialValue: gameDTO.rows ?? 3,
isSmall: true, isSmall: true,
maxLength: 2, maxLength: 2,
onChanged: (value) { onChanged: (value) {
@ -128,7 +175,8 @@ class _GameConfigState extends State<GameConfig> {
widget.onChanged(gameDTO); widget.onChanged(gameDTO);
}); });
} catch (e) { } catch (e) {
showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null); showNotification(Colors.orange, kWhite,
'Cela doit être un chiffre', context, null);
} }
}, },
), ),
@ -137,7 +185,7 @@ class _GameConfigState extends State<GameConfig> {
height: 100, height: 100,
child: NumberInputContainer( child: NumberInputContainer(
label: "Nombre de colonnes :", label: "Nombre de colonnes :",
initialValue: gameDTO.cols!, initialValue: gameDTO.cols ?? 3,
isSmall: true, isSmall: true,
maxLength: 2, maxLength: 2,
onChanged: (value) { onChanged: (value) {
@ -147,14 +195,18 @@ class _GameConfigState extends State<GameConfig> {
widget.onChanged(gameDTO); widget.onChanged(gameDTO);
}); });
} catch (e) { } catch (e) {
showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null); showNotification(Colors.orange, kWhite,
'Cela doit être un chiffre', context, null);
} }
}, },
), ),
), ),
],)
], ],
), ),
],
),
],
],
); );
} }
} }

View File

@ -6,7 +6,6 @@ import 'package:manager_app/Components/confirmation_dialog.dart';
import 'package:manager_app/Components/geoloc_input_container.dart'; import 'package:manager_app/Components/geoloc_input_container.dart';
import 'package:manager_app/Components/common_loader.dart'; import 'package:manager_app/Components/common_loader.dart';
import 'package:manager_app/Components/message_notification.dart'; import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/Components/multi_select_dropdown_language_container.dart';
import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/category_input_container.dart'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/category_input_container.dart';
import 'package:manager_app/Components/dropDown_input_container.dart'; import 'package:manager_app/Components/dropDown_input_container.dart';
@ -17,12 +16,11 @@ import 'package:manager_app/Components/single_select_container.dart';
import 'package:manager_app/Components/slider_input_container.dart'; import 'package:manager_app/Components/slider_input_container.dart';
import 'package:manager_app/Components/string_input_container.dart'; import 'package:manager_app/Components/string_input_container.dart';
import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/showNewOrUpdateGeoPoint.dart'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/showNewOrUpdateGeoPoint.dart';
import 'package:html/parser.dart' show parse;
import 'package:manager_app/app_context.dart'; import 'package:manager_app/app_context.dart';
import 'package:manager_app/client.dart'; import 'package:manager_app/client.dart';
import 'package:manager_app/constants.dart'; import 'package:manager_app/constants.dart';
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
import 'dart:convert'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Parcours/parcours_config.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -52,7 +50,8 @@ class _MapConfigState extends State<MapConfig> {
String filterSearch = ''; String filterSearch = '';
final ValueNotifier<List<int>?> selectedCategoriesNotifier = ValueNotifier([]); final ValueNotifier<List<int>?> selectedCategoriesNotifier =
ValueNotifier([]);
final ValueNotifier<String?> searchNotifier = ValueNotifier(""); final ValueNotifier<String?> searchNotifier = ValueNotifier("");
@override @override
@ -126,7 +125,25 @@ class _MapConfigState extends State<MapConfig> {
break; break;
} }
return return DefaultTabController(
length: 2,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TabBar(
labelColor: kPrimaryColor,
unselectedLabelColor: Colors.grey,
indicatorColor: kPrimaryColor,
tabs: [
Tab(icon: Icon(Icons.map), text: "Points d'Intérêt"),
Tab(icon: Icon(Icons.route), text: "Parcours"),
],
),
Container(
height: 500, // Reduced from 550 to 500 to avoid 8.5px overflow
child: TabBarView(
children: [
// Tab 1: Configuration & Points
SingleChildScrollView( SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
@ -137,7 +154,8 @@ class _MapConfigState extends State<MapConfig> {
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [ children: [
SingleSelectContainer( SingleSelectContainer(
label: "Service :", label: "Service :",
@ -147,31 +165,38 @@ class _MapConfigState extends State<MapConfig> {
onChanged: (String value) { onChanged: (String value) {
switch (value) { switch (value) {
case "Google": case "Google":
mapDTO.mapProvider = MapProvider.Google; mapDTO.mapProvider =
MapProvider.Google;
break; break;
case "MapBox": case "MapBox":
mapDTO.mapProvider = MapProvider.MapBox; mapDTO.mapProvider =
MapProvider.MapBox;
break; break;
} }
//widget.onChanged(jsonEncode(mapDTO).toString());
widget.onChanged(mapDTO); widget.onChanged(mapDTO);
} }),
),
GeolocInputContainer( GeolocInputContainer(
label: "Point de centrage:", label: "Point de centrage:",
initialValue: mapDTO.centerLatitude != null && mapDTO.centerLongitude != null ? LatLong(double.parse(mapDTO.centerLatitude!), double.parse(mapDTO.centerLongitude!)) : null, initialValue:
mapDTO.centerLatitude != null &&
mapDTO.centerLongitude != null
? LatLong(
double.parse(
mapDTO.centerLatitude!),
double.parse(
mapDTO.centerLongitude!))
: null,
color: kPrimaryColor, color: kPrimaryColor,
onChanged: (LatLong? localisation) { onChanged: (LatLong? localisation) {
if (localisation != null) { if (localisation != null) {
mapDTO.centerLongitude = localisation.longitude.toString(); mapDTO.centerLongitude =
mapDTO.centerLatitude = localisation.latitude.toString(); localisation.longitude.toString();
mapDTO.centerLatitude =
localisation.latitude.toString();
} }
//widget.onChanged(jsonEncode(mapDTO).toString());
widget.onChanged(mapDTO); widget.onChanged(mapDTO);
}, },
isSmall: true isSmall: true),
),
// Icon
ResourceInputContainer( ResourceInputContainer(
label: "Icône:", label: "Icône:",
initialValue: mapDTO.iconResourceId, initialValue: mapDTO.iconResourceId,
@ -185,25 +210,21 @@ class _MapConfigState extends State<MapConfig> {
mapDTO.iconResourceId = resource.id; mapDTO.iconResourceId = resource.id;
mapDTO.iconSource = resource.url; mapDTO.iconSource = resource.url;
} }
//widget.onChanged(jsonEncode(mapDTO).toString());
widget.onChanged(mapDTO); widget.onChanged(mapDTO);
}, },
isSmall: true isSmall: true),
), ]),
]
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
if (mapDTO.mapProvider == MapProvider.Google) if (mapDTO.mapProvider == MapProvider.Google)
// Map Type
DropDownInputContainer( DropDownInputContainer(
label: "Type:", label: "Type:",
values: map_types, values: map_types,
initialValue: mapType, initialValue: mapType,
onChange: (String? value) { onChange: (String? value) {
mapDTO.mapType = MapTypeApp.fromJson(value); mapDTO.mapType =
//widget.onChanged(jsonEncode(mapDTO).toString()); MapTypeApp.fromJson(value);
widget.onChanged(mapDTO); widget.onChanged(mapDTO);
}, },
), ),
@ -213,21 +234,21 @@ class _MapConfigState extends State<MapConfig> {
values: map_types_mapBox, values: map_types_mapBox,
initialValue: mapTypeMapBox, initialValue: mapTypeMapBox,
onChange: (String? value) { onChange: (String? value) {
mapDTO.mapTypeMapbox = MapTypeMapBox.fromJson(value); mapDTO.mapTypeMapbox =
//widget.onChanged(jsonEncode(mapDTO).toString()); MapTypeMapBox.fromJson(value);
widget.onChanged(mapDTO); widget.onChanged(mapDTO);
}, },
), ),
// Zoom
SliderInputContainer( SliderInputContainer(
label: "Zoom:", label: "Zoom:",
initialValue: mapDTO.zoom != null ? mapDTO.zoom!.toDouble() : 18, initialValue: mapDTO.zoom != null
? mapDTO.zoom!.toDouble()
: 18,
color: kPrimaryColor, color: kPrimaryColor,
min: 0, min: 0,
max: 30, max: 30,
onChanged: (double value) { onChanged: (double value) {
mapDTO.zoom = value.toInt(); mapDTO.zoom = value.toInt();
//widget.onChanged(jsonEncode(mapDTO).toString());
widget.onChanged(mapDTO); widget.onChanged(mapDTO);
}, },
), ),
@ -235,18 +256,21 @@ class _MapConfigState extends State<MapConfig> {
height: 70, height: 70,
child: CategoryInputContainer( child: CategoryInputContainer(
label: "Catégories :", label: "Catégories :",
initialValue: mapDTO.categories != null ? mapDTO.categories! : [], initialValue: mapDTO.categories != null
? mapDTO.categories!
: [],
color: kPrimaryColor, color: kPrimaryColor,
onChanged: (List<CategorieDTO>? value) { onChanged: (List<CategorieDTO>? value) {
if (value != null) { if (value != null) {
mapDTO.categories = value; mapDTO.categories = value;
//update point's category
if (mapDTO.points != null) { if (mapDTO.points != null) {
mapDTO.points!.forEach((p) { mapDTO.points!.forEach((p) {
// Check if category still exist - Delete if (p.categorieId != null &&
if(p.categorieId != null && !mapDTO.categories!.map((c) => c.id).any((e) => e != null && e == p.categorieId)) !mapDTO.categories!
{ .map((c) => c.id)
.any((e) =>
e != null &&
e == p.categorieId)) {
p.categorieId = null; p.categorieId = null;
} }
}); });
@ -262,63 +286,143 @@ class _MapConfigState extends State<MapConfig> {
), ),
), ),
FutureBuilder( FutureBuilder(
future: getGeoPoints((appContext.getContext() as ManagerAppContext).clientAPI!), future: getGeoPoints(
(appContext.getContext() as ManagerAppContext)
.clientAPI!),
builder: (context, AsyncSnapshot<dynamic> snapshot) { builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(child: CommonLoader()); return Center(child: CommonLoader());
} else { } else {
if(snapshot.connectionState == ConnectionState.done) if (snapshot.connectionState ==
{ ConnectionState.done) {
mapDTO.points = snapshot.data; mapDTO.points = snapshot.data;
pointsToShow = mapDTO.points!; pointsToShow = mapDTO.points!;
pointsToShow.sort((a, b) => a.title!.firstWhere((t) => t.language == 'FR').value!.toLowerCase().compareTo(b.title!.firstWhere((t) => t.language == 'FR').value!.toLowerCase())); pointsToShow.sort((a, b) => a.title!
selectedCategoriesNotifier.value = mapDTO.categories!.map((categorie) => categorie.id!).toList(); .firstWhere((t) => t.language == 'FR')
.value!
.toLowerCase()
.compareTo(b.title!
.firstWhere((t) => t.language == 'FR')
.value!
.toLowerCase()));
selectedCategoriesNotifier.value = mapDTO
.categories!
.map((categorie) => categorie.id!)
.toList();
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25), borderRadius: BorderRadius.circular(25),
border: Border.all(width: 1.5, color: kSecond) border: Border.all(
), width: 1.5, color: kSecond)),
child: Stack( child: Stack(children: [
children: [
Container( Container(
constraints: BoxConstraints(minHeight: 100), constraints:
BoxConstraints(minHeight: 100),
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 95, left: 10, right: 10, bottom: 10), padding: const EdgeInsets.only(
child: ValueListenableBuilder<String?>( top: 95,
valueListenable: searchNotifier, left: 10,
builder: (context, searchValue, child) { right: 10,
return ValueListenableBuilder<List<int>?>( bottom: 10),
valueListenable: selectedCategoriesNotifier, child:
builder: (context, selectedCategories, child) { ValueListenableBuilder<String?>(
if(selectedCategories == null || selectedCategories.length == 0) { valueListenable:
pointsToShow = mapDTO.points!.where((point) => point.categorieId == null).toList(); searchNotifier,
builder: (context,
searchValue, child) {
return ValueListenableBuilder<
List<int>?>(
valueListenable:
selectedCategoriesNotifier,
builder: (context,
selectedCategories,
child) {
if (selectedCategories ==
null ||
selectedCategories
.length ==
0) {
pointsToShow = mapDTO
.points!
.where((point) =>
point
.categorieId ==
null)
.toList();
} else { } else {
pointsToShow = mapDTO.points!.where((point) => selectedCategories.any((tps) => point.categorieId == null || point.categorieId == tps)).toList(); pointsToShow = mapDTO
.points!
.where((point) =>
selectedCategories.any((tps) =>
point.categorieId ==
null ||
point.categorieId ==
tps))
.toList();
} }
pointsToShow = searchValue != null && searchValue.trim().isNotEmpty ? pointsToShow.where((GeoPointDTO pointGeo) => removeDiacritics(pointGeo.title!.firstWhere((t) => t.language == "FR").value!.toUpperCase()).contains(removeDiacritics(searchValue.toUpperCase()))).toList() : pointsToShow; pointsToShow = searchValue !=
null &&
searchValue
.trim()
.isNotEmpty
? pointsToShow
.where((GeoPointDTO pointGeo) => removeDiacritics(pointGeo
.title!
.firstWhere((t) =>
t.language ==
"FR")
.value!
.toUpperCase())
.contains(
removeDiacritics(
searchValue.toUpperCase())))
.toList()
: pointsToShow;
return GridView.builder( return GridView
shrinkWrap: true, .builder(
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 8), shrinkWrap:
itemCount: pointsToShow.length, true,
itemBuilder: (BuildContext context, int index) { gridDelegate:
return new SliverGridDelegateWithFixedCrossAxisCount(
Container( crossAxisCount:
decoration: boxDecoration(pointsToShow[index], appContext), 8),
padding: const EdgeInsets.all(5), itemCount:
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 10), pointsToShow
child: getElement(index, pointsToShow[index], size, appContext), .length,
itemBuilder:
(BuildContext
context,
int index) {
return Container(
decoration: boxDecoration(
pointsToShow[
index],
appContext),
padding:
const EdgeInsets
.all(
5),
margin: EdgeInsets.symmetric(
vertical:
10,
horizontal:
10),
child: getElement(
index,
pointsToShow[
index],
size,
appContext),
); );
} });
); });
} }),
);
}
),
), ),
), ),
Positioned( Positioned(
@ -336,22 +440,52 @@ class _MapConfigState extends State<MapConfig> {
label: null, label: null,
color: kSecond, color: kSecond,
width: size.width * 0.45, width: size.width * 0.45,
initialValue: mapDTO.categories!.where((cat) => selectedCategoriesNotifier.value!.contains(cat.id)).map((categorie) => categorie.label!.firstWhere((element) => element.language == 'FR').value!).toList(), initialValue: mapDTO.categories!
.where((cat) =>
selectedCategoriesNotifier
.value!
.contains(cat.id))
.map((categorie) => categorie
.label!
.firstWhere((element) =>
element.language ==
'FR')
.value!)
.toList(),
isMultiple: true, isMultiple: true,
isHTMLLabel: true, isHTMLLabel: true,
values: mapDTO.categories!.map((categorie) => categorie.label!.firstWhere((element) => element.language == 'FR').value!).toList(), values: mapDTO.categories!
.map((categorie) => categorie
.label!
.firstWhere((element) =>
element.language ==
'FR')
.value!)
.toList(),
onChanged: (value) { onChanged: (value) {
var tempOutput = new List<String>.from(value); var tempOutput =
selectedCategoriesNotifier.value = mapDTO.categories!.where((c) => tempOutput.contains(c.label!.firstWhere((element) => element.language == 'FR').value!)).map((cat) => cat.id!).toList(); new List<String>.from(value);
selectedCategoriesNotifier.value =
mapDTO.categories!
.where((c) => tempOutput
.contains(c.label!
.firstWhere(
(element) =>
element
.language ==
'FR')
.value!))
.map((cat) => cat.id!)
.toList();
}, },
) )),
),
Positioned( Positioned(
top: 0, top: 0,
right: 150, right: 150,
child: Container( child: Container(
height: size.height * 0.1, height: size.height * 0.1,
constraints: BoxConstraints(minHeight: 85), constraints:
BoxConstraints(minHeight: 85),
child: StringInputContainer( child: StringInputContainer(
label: "Recherche:", label: "Recherche:",
isSmall: true, isSmall: true,
@ -369,42 +503,44 @@ class _MapConfigState extends State<MapConfig> {
child: InkWell( child: InkWell(
onTap: () { onTap: () {
showNewOrUpdateGeoPoint( showNewOrUpdateGeoPoint(
mapDTO, mapDTO, null,
null,
(GeoPointDTO geoPoint) async { (GeoPointDTO geoPoint) async {
/*setState(() {
mapDTO.points!.add(geoPoint);
mapDTO.points!.sort((a, b) => a.title!.firstWhere((t) => t.language == 'FR').value!.toLowerCase().compareTo(b.title!.firstWhere((t) => t.language == 'FR').value!.toLowerCase()));
if(selectedCategoriesNotifier.value == null || selectedCategoriesNotifier.value!.length == 0) {
//pointsToShow = mapDTO.points!.where((point) => point.categorie == null).toList();
pointsToShow = mapDTO.points!.where((point) => point.categorieId == null).toList();
} else {
//pointsToShow = mapDTO.points!.where((point) => selectedCategories!.any((tps) => point.categorie == null || point.categorie?.label?.firstWhere((lab) => lab.language == 'FR').value == tps)).toList();
pointsToShow = mapDTO.points!.where((point) => selectedCategoriesNotifier.value!.any((tps) => point.categorieId == null || point.categorieId == tps)).toList();
}
//widget.onChanged(jsonEncode(mapDTO).toString());
widget.onChanged(mapDTO);
});*/
try { try {
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionMapApi!.sectionMapCreate(mapDTO.id!, geoPoint); await (appContext.getContext()
showNotification(kSuccess, kWhite, 'Le point géographique a été créé avec succès', context, null); as ManagerAppContext)
.clientAPI!
.sectionMapApi!
.sectionMapCreate(
mapDTO.id!, geoPoint);
showNotification(
kSuccess,
kWhite,
'Le point géographique a été créé avec succès',
context,
null);
setState(() { setState(() {
// refresh ui // refresh ui
print("Refresh UI"); print("Refresh UI");
}); });
} catch (e) { } catch (e) {
showNotification(kError, kWhite, 'Une erreur est survenue lors de la création du point géographique', context, null); showNotification(
kError,
kWhite,
'Une erreur est survenue lors de la création du point géographique',
context,
null);
} }
}, }, appContext, context);
appContext,
context);
}, },
child: Container( child: Container(
height: MediaQuery.of(context).size.width * 0.04, height: MediaQuery.of(context)
width: MediaQuery.of(context).size.width * 0.04, .size
.width *
0.04,
width: MediaQuery.of(context)
.size
.width *
0.04,
child: Icon( child: Icon(
Icons.add, Icons.add,
color: kTextLightColor, color: kTextLightColor,
@ -413,13 +549,15 @@ class _MapConfigState extends State<MapConfig> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: kSuccess, color: kSuccess,
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(20.0), borderRadius:
BorderRadius.circular(20.0),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: kSecond, color: kSecond,
spreadRadius: 0.5, spreadRadius: 0.5,
blurRadius: 5, blurRadius: 5,
offset: Offset(0, 1.5), // changes position of shadow offset: Offset(0,
1.5), // changes position of shadow
), ),
], ],
), ),
@ -430,10 +568,30 @@ class _MapConfigState extends State<MapConfig> {
), ),
); );
} else { } else {
return Center(child: Text("Une erreur est survenue lors de la récupération des points géographiques"),); return Center(
} child: Text(
"Une erreur est survenue lors de la récupération des points géographiques"),
);
} }
} }
}),
],
),
),
// Tab 2: Parcours
ParcoursConfig(
initialValue: mapDTO.guidedPaths ?? [],
parentId: mapDTO.id!,
isEvent: false,
onChanged: (paths) {
setState(() {
mapDTO.guidedPaths = paths;
widget.onChanged(mapDTO);
});
},
),
],
),
), ),
], ],
), ),
@ -441,7 +599,8 @@ class _MapConfigState extends State<MapConfig> {
} }
Future<List<GeoPointDTO>?> getGeoPoints(Client client) async { Future<List<GeoPointDTO>?> getGeoPoints(Client client) async {
List<GeoPointDTO>? geoPoints = await client.sectionMapApi!.sectionMapGetAllGeoPointsFromSection(widget.initialValue.id!); List<GeoPointDTO>? geoPoints = await client.sectionMapApi!
.sectionMapGetAllGeoPointsFromSection(widget.initialValue.id!);
return geoPoints ?? []; return geoPoints ?? [];
} }
@ -455,13 +614,15 @@ class _MapConfigState extends State<MapConfig> {
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: HtmlWidget( child: HtmlWidget(
point != null ? point.title == null ? "" : point.title![0].value! : "", point != null
? point.title == null
? ""
: point.title![0].value!
: "",
//textAlign: TextAlign.left, //textAlign: TextAlign.left,
customStylesBuilder: (element) { customStylesBuilder: (element) {
return {'text-align': 'center'}; return {'text-align': 'center'};
}, }, textStyle: TextStyle(fontSize: 20)),
textStyle: TextStyle(fontSize: 20)
),
), ),
), ),
Positioned( Positioned(
@ -478,19 +639,30 @@ class _MapConfigState extends State<MapConfig> {
left: 0, left: 0,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
showNewOrUpdateGeoPoint( showNewOrUpdateGeoPoint(mapDTO, pointsToShow[index],
mapDTO,
pointsToShow[index],
(GeoPointDTO geoPoint) async { (GeoPointDTO geoPoint) async {
try { try {
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionMapApi!.sectionMapUpdate(geoPoint); await (appContext.getContext() as ManagerAppContext)
showNotification(kSuccess, kWhite, 'Le point géographique a été mis à jour avec succès', context, null); .clientAPI!
.sectionMapApi!
.sectionMapUpdate(geoPoint);
showNotification(
kSuccess,
kWhite,
'Le point géographique a été mis à jour avec succès',
context,
null);
setState(() { setState(() {
// refresh ui // refresh ui
print("Refresh UI"); print("Refresh UI");
}); });
} catch (e) { } catch (e) {
showNotification(kError, kWhite, 'Une erreur est survenue lors de la mise à jour du point géographique', context, null); showNotification(
kError,
kWhite,
'Une erreur est survenue lors de la mise à jour du point géographique',
context,
null);
} }
/*setState(() { /*setState(() {
@ -503,16 +675,13 @@ class _MapConfigState extends State<MapConfig> {
//widget.onChanged(jsonEncode(mapDTO).toString()); //widget.onChanged(jsonEncode(mapDTO).toString());
widget.onChanged(mapDTO); widget.onChanged(mapDTO);
});*/ });*/
}, }, appContext, context);
appContext,
context);
}, },
child: Icon( child: Icon(
Icons.edit, Icons.edit,
color: kPrimaryColor, color: kPrimaryColor,
size: 20.0, size: 20.0,
) )),
),
), ),
Positioned( Positioned(
bottom: 0, bottom: 0,
@ -521,30 +690,38 @@ class _MapConfigState extends State<MapConfig> {
onTap: () async { onTap: () async {
showConfirmationDialog( showConfirmationDialog(
"Êtes-vous sûr de vouloir supprimer ce point géographique ?", "Êtes-vous sûr de vouloir supprimer ce point géographique ?",
() {}, () {}, () async {
() async {
try { try {
var pointToRemove = pointsToShow[index]; var pointToRemove = pointsToShow[index];
(appContext.getContext() as ManagerAppContext).clientAPI!.sectionMapApi!.sectionMapDelete(pointToRemove.id!); (appContext.getContext() as ManagerAppContext)
showNotification(kSuccess, kWhite, 'Le point géographique a été supprimé avec succès', context, null); .clientAPI!
.sectionMapApi!
.sectionMapDelete(pointToRemove.id!);
showNotification(
kSuccess,
kWhite,
'Le point géographique a été supprimé avec succès',
context,
null);
// refresh UI // refresh UI
setState(() { setState(() {
print("Refresh UI"); print("Refresh UI");
}); });
} catch (e) { } catch (e) {
showNotification(kError, kWhite, 'Une erreur est survenue lors de la suppression du point géographique', context, null); showNotification(
kError,
kWhite,
'Une erreur est survenue lors de la suppression du point géographique',
context,
null);
} }
}, }, context);
context
);
}, },
child: Icon( child: Icon(
Icons.delete, Icons.delete,
color: kError, color: kError,
size: 20.0, size: 20.0,
) )),
),
) )
], ],
), ),
@ -552,7 +729,6 @@ class _MapConfigState extends State<MapConfig> {
} }
} }
boxDecoration(GeoPointDTO geoPointDTO, appContext) { boxDecoration(GeoPointDTO geoPointDTO, appContext) {
return BoxDecoration( return BoxDecoration(
color: kBackgroundColor, color: kBackgroundColor,

View File

@ -1,19 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:location_picker_flutter_map/location_picker_flutter_map.dart'; import 'package:manager_app/Components/geometry_input_container.dart';
import 'package:manager_app/Components/dropDown_input_container_categories.dart'; import 'package:manager_app/Components/dropDown_input_container_categories.dart';
import 'package:manager_app/Components/common_loader.dart';
import 'package:manager_app/Components/multi_string_input_container.dart'; import 'package:manager_app/Components/multi_string_input_container.dart';
import 'package:manager_app/Components/resource_input_container.dart'; import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_app/Components/rounded_button.dart'; import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Components/string_input_container.dart';
import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/geopoint_image_list.dart'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/geopoint_image_list.dart';
import 'package:manager_app/app_context.dart'; import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart'; import 'package:manager_app/constants.dart';
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Function getResult, AppContext appContext, BuildContext context) { void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO,
GeoPointDTO geoPointDTO = new GeoPointDTO(); Function getResult, AppContext appContext, BuildContext context) {
GeoPointDTO geoPointDTO = GeoPointDTO();
if (inputGeoPointDTO != null) { if (inputGeoPointDTO != null) {
geoPointDTO = inputGeoPointDTO; geoPointDTO = inputGeoPointDTO;
@ -21,262 +20,222 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
geoPointDTO.title = <TranslationDTO>[]; geoPointDTO.title = <TranslationDTO>[];
geoPointDTO.description = <TranslationDTO>[]; geoPointDTO.description = <TranslationDTO>[];
geoPointDTO.contents = <ContentDTO>[]; geoPointDTO.contents = <ContentDTO>[];
geoPointDTO.schedules = <TranslationDTO>[];
geoPointDTO.prices = <TranslationDTO>[];
geoPointDTO.phone = <TranslationDTO>[];
geoPointDTO.email = <TranslationDTO>[];
geoPointDTO.site = <TranslationDTO>[];
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
managerAppContext.selectedConfiguration!.languages!.forEach((element) { managerAppContext.selectedConfiguration!.languages!.forEach((element) {
var translationDTO = new TranslationDTO(); var translationDTO = TranslationDTO();
translationDTO.language = element; translationDTO.language = element;
translationDTO.value = ""; translationDTO.value = "";
geoPointDTO.title!.add(translationDTO); geoPointDTO.title!.add(translationDTO);
geoPointDTO.description!.add(translationDTO); geoPointDTO.description!.add(translationDTO);
geoPointDTO.schedules!.add(translationDTO);
geoPointDTO.prices!.add(translationDTO);
geoPointDTO.phone!.add(translationDTO);
geoPointDTO.email!.add(translationDTO);
geoPointDTO.site!.add(translationDTO);
}); });
} }
var defaultPosition = LatLong(50.429333, 4.891434); // Default Namur
var pointPosition;
if(geoPointDTO.geometry != null) {
switch(geoPointDTO.geometry!.type) {
case "Point":
var coordinates = geoPointDTO.geometry!.coordinates as List<dynamic>;
pointPosition = LatLong(coordinates.first, coordinates.last);
break;
case "LineString":
pointPosition = LatLong(50.429333, 4.891434); // TODO default Namur
break;
case "Polygon":
pointPosition = LatLong(50.429333, 4.891434); // TODO default Namur
break;
}
}
LatLong initPosition = geoPointDTO.geometry == null ? defaultPosition : pointPosition;
Size size = MediaQuery.of(context).size;
showDialog( showDialog(
builder: (BuildContext context) => AlertDialog( context: context,
shape: RoundedRectangleBorder( builder: (BuildContext context) {
borderRadius: BorderRadius.all(Radius.circular(20.0)) 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.88;
final double contentWidth =
dialogWidth - 48; // 24px padding each side
final double thirdWidth = (contentWidth - 40) / 3;
return Dialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
width: dialogWidth,
constraints: BoxConstraints(maxHeight: screenHeight * 0.9),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Titre du dialog
Text(
"Point géographique / Zone",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: kPrimaryColor),
), ),
content: Container( SizedBox(height: 16),
width: size.width *0.85, // Corps scrollable
Flexible(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Point géographique", style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)), // Géométrie
Column( GeometryInputContainer(
children: [ label: "Géométrie (Point/Ligne/Zone) :",
Container( initialGeometry: geoPointDTO.geometry,
width: size.width *0.75, initialColor: geoPointDTO.polyColor,
height: 350, onSave: (geometry, color) {
child: FlutterLocationPicker( setState(() {
initZoom: 14, geoPointDTO.geometry = geometry;
initPosition: initPosition, geoPointDTO.polyColor = color;
minZoomLevel: 0, });
maxZoomLevel: 17,
markerIcon: const Icon(
Icons.location_pin,
color: kPrimaryColor,
size: 50,
),
loadingWidget: CommonLoader(iconSize: 40.0),
searchBarHintColor: kPrimaryColor,
mapLoadingBackgroundColor: kSecond,
zoomButtonsBackgroundColor: kPrimaryColor,
zoomButtonsColor: Colors.white,
locationButtonBackgroundColor: kPrimaryColor,
locationButtonsColor: Colors.white,
countryFilter: "be, fr",
trackMyPosition: false,
searchBarHintText: "Chercher une localisation",
searchBarTextColor: Colors.black,
searchBarBackgroundColor: Colors.white,
showSelectLocationButton : true,
selectLocationButtonText: "Choisir cette localisation",
selectedLocationButtonTextstyle: const TextStyle(fontSize: 18, color: kPrimaryColor),
mapLanguage: 'fr',
onError: (e) => print(e),
selectLocationButtonLeadingIcon: const Icon(Icons.check, color: kPrimaryColor),
onPicked: (pickedData) {
geoPointDTO.geometry = new GeometryDTO(type: "Point", coordinates: [pickedData.latLong.latitude, pickedData.latLong.longitude]);
}, },
onChanged: (pickedData) {
print("onChanged");
geoPointDTO.geometry = new GeometryDTO(type: "Point", coordinates: [pickedData.latLong.latitude, pickedData.latLong.longitude]);
/*print(pickedData.latLong.latitude);
print(pickedData.latLong.longitude);
print(pickedData.address);
print(pickedData.addressData);*/
},
showContributorBadgeForOSM: false,
), ),
), SizedBox(height: 12),
/*Row( // Ligne 1 : Titre | Description | Site
mainAxisAlignment: MainAxisAlignment.spaceAround, Row(
children: [ children: [
SizedBox( SizedBox(
height: 100, width: thirdWidth,
child: StringInputContainer(
isSmall: true,
label: "Latitude (#.#):",
initialValue: geoPointDTO.latitude,
onChanged: (value) {
geoPointDTO.latitude = value;
},
),
),
SizedBox(
height: 100,
child: StringInputContainer(
isSmall: true,
label: "Longitude (#.#):",
initialValue: geoPointDTO.longitude,
onChanged: (value) {
geoPointDTO.longitude = value;
},
),
)
],
),*/
Container(
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
child: MultiStringInputContainer( child: MultiStringInputContainer(
label: "Titre affiché:", label: "Titre :",
modalLabel: "Titre", modalLabel: "Titre",
fontSize: 20, fontSize: 16,
isHTML: true, isHTML: true,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: geoPointDTO.title != null ? geoPointDTO.title! : [], initialValue: geoPointDTO.title ?? [],
onGetResult: (value) { onGetResult: (value) {
if (geoPointDTO.title != value) {
geoPointDTO.title = value; geoPointDTO.title = value;
}
}, },
maxLines: 1, maxLines: 1,
isTitle: true isTitle: true,
), ),
), ),
Container( SizedBox(width: 20),
constraints: BoxConstraints(minHeight: 50, maxHeight: 80), SizedBox(
width: thirdWidth,
child: MultiStringInputContainer( child: MultiStringInputContainer(
label: "Description affichée:", label: "Description :",
modalLabel: "Description", modalLabel: "Description",
fontSize: 20, fontSize: 16,
isHTML: true, isHTML: true,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: geoPointDTO.description != null ? geoPointDTO.description! : [], initialValue: geoPointDTO.description ?? [],
isMandatory: false, isMandatory: false,
onGetResult: (value) { onGetResult: (value) {
if (geoPointDTO.description != value) {
geoPointDTO.description = value; geoPointDTO.description = value;
}
}, },
maxLines: 1, maxLines: 1,
isTitle: false isTitle: false,
), ),
), ),
Container( SizedBox(width: 20),
constraints: BoxConstraints(minHeight: 50, maxHeight: 80), SizedBox(
width: thirdWidth,
child: MultiStringInputContainer( child: MultiStringInputContainer(
label: "Site :", label: "Site :",
modalLabel: "Site web", modalLabel: "Site web",
fontSize: 20, fontSize: 16,
isHTML: true, isHTML: true,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: geoPointDTO.site != null ? geoPointDTO.site! : [], initialValue: geoPointDTO.site ?? [],
isMandatory: false, isMandatory: false,
onGetResult: (value) { onGetResult: (value) {
if (geoPointDTO.site != value) {
geoPointDTO.site = value; geoPointDTO.site = value;
}
}, },
maxLines: 1, maxLines: 1,
isTitle: true isTitle: true,
), ),
), ),
], ],
), ),
), SizedBox(height: 12),
Container( // Ligne 2 : Prix | Tel | Email
height: 100, Row(
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Container( SizedBox(
constraints: BoxConstraints(minHeight: 50, maxHeight: 80), width: thirdWidth,
child: MultiStringInputContainer( child: MultiStringInputContainer(
label: "Prix :", label: "Prix :",
modalLabel: "Prix", modalLabel: "Prix",
fontSize: 20, fontSize: 16,
isHTML: true, isHTML: true,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: geoPointDTO.prices != null ? geoPointDTO.prices! : [], initialValue: geoPointDTO.prices ?? [],
isMandatory: false, isMandatory: false,
onGetResult: (value) { onGetResult: (value) {
if (geoPointDTO.prices != value) {
geoPointDTO.prices = value; geoPointDTO.prices = value;
}
}, },
maxLines: 1, maxLines: 1,
isTitle: false isTitle: false,
), ),
), ),
Container( SizedBox(width: 20),
constraints: BoxConstraints(minHeight: 50, maxHeight: 80), SizedBox(
width: thirdWidth,
child: MultiStringInputContainer( child: MultiStringInputContainer(
label: "Téléphone:", label: "Tel :",
modalLabel: "Téléphone", modalLabel: "Téléphone",
fontSize: 20, fontSize: 16,
isHTML: true, isHTML: true,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: geoPointDTO.phone != null ? geoPointDTO.phone! : [], initialValue: geoPointDTO.phone ?? [],
isMandatory: false, isMandatory: false,
onGetResult: (value) { onGetResult: (value) {
if (geoPointDTO.phone != value) {
geoPointDTO.phone = value; geoPointDTO.phone = value;
}
}, },
maxLines: 1, maxLines: 1,
isTitle: true isTitle: true,
), ),
), ),
Container( SizedBox(width: 20),
constraints: BoxConstraints(minHeight: 50, maxHeight: 80), SizedBox(
width: thirdWidth,
child: MultiStringInputContainer( child: MultiStringInputContainer(
label: "Email :", label: "Email :",
modalLabel: "Email", modalLabel: "Email",
fontSize: 20, fontSize: 16,
isHTML: true, isHTML: true,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: geoPointDTO.email != null ? geoPointDTO.email! : [], initialValue: geoPointDTO.email ?? [],
isMandatory: false, isMandatory: false,
onGetResult: (value) { onGetResult: (value) {
if (geoPointDTO.email != value) {
geoPointDTO.email = value; geoPointDTO.email = value;
}
}, },
maxLines: 1, maxLines: 1,
isTitle: true isTitle: true,
), ),
), ),
], ],
), ),
), SizedBox(height: 12),
Container( // Ligne 3 : Horaires | Image | Catégorie
height: 100, Row(
width: double.infinity, crossAxisAlignment: CrossAxisAlignment.start,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
ResourceInputContainer( SizedBox(
label: "Image principal:", width: thirdWidth,
initialValue: geoPointDTO.imageResourceId != null ? geoPointDTO.imageResourceId : null, child: MultiStringInputContainer(
label: "Horaires :",
modalLabel: "Horaires d'ouverture",
fontSize: 16,
isHTML: true,
color: kPrimaryColor,
initialValue: geoPointDTO.schedules ?? [],
isMandatory: false,
onGetResult: (value) {
geoPointDTO.schedules = value;
},
maxLines: 1,
isTitle: false,
),
),
SizedBox(width: 20),
SizedBox(
width: thirdWidth,
child: ResourceInputContainer(
label: "Image :",
initialValue: geoPointDTO.imageResourceId,
color: kPrimaryColor, color: kPrimaryColor,
onChanged: (ResourceDTO resource) { onChanged: (ResourceDTO resource) {
if (resource.id == null) { if (resource.id == null) {
@ -288,29 +247,33 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
} }
}, },
), ),
if(mapDTO.categories != null && mapDTO.categories!.isNotEmpty) ),
Container( SizedBox(width: 20),
constraints: BoxConstraints(minHeight: 50, maxHeight: 80), if (mapDTO.categories != null &&
mapDTO.categories!.isNotEmpty)
SizedBox(
width: thirdWidth,
child: DropDownInputContainerCategories( child: DropDownInputContainerCategories(
label: "Choisir une catégorie:", label: "Catégorie :",
categories: mapDTO.categories!, categories: mapDTO.categories!,
initialValue: geoPointDTO.categorieId, initialValue: geoPointDTO.categorieId,
onChange: (CategorieDTO? value) { onChange: (CategorieDTO? value) {
if(value != null && value.order != -1) if (value != null && value.order != -1) {
{
geoPointDTO.categorieId = value.id; geoPointDTO.categorieId = value.id;
} else } else {
{
geoPointDTO.categorieId = null; geoPointDTO.categorieId = null;
} }
}, },
), ),
), )
else
SizedBox(width: thirdWidth),
], ],
), ),
), SizedBox(height: 12),
// Liste de contenus
Container( Container(
height: size.height * 0.33, height: screenHeight * 0.28,
decoration: BoxDecoration( decoration: BoxDecoration(
color: kWhite, color: kWhite,
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
@ -321,7 +284,7 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
color: kSecond, color: kSecond,
spreadRadius: 0.5, spreadRadius: 0.5,
blurRadius: 5, blurRadius: 5,
offset: Offset(0, 1.5), // changes position of shadow offset: Offset(0, 1.5),
), ),
], ],
), ),
@ -334,19 +297,15 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
), ),
], ],
), ),
],
), ),
), ),
), SizedBox(height: 16),
actions: <Widget>[ // Boutons
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
Align( SizedBox(
alignment: AlignmentDirectional.bottomEnd, height: 46,
child: Container(
width: 175,
height: 70,
child: RoundedButton( child: RoundedButton(
text: "Annuler", text: "Annuler",
icon: Icons.undo, icon: Icons.undo,
@ -357,34 +316,38 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
} }
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
fontSize: 20, fontSize: 15,
horizontal: 24,
), ),
), ),
), SizedBox(width: 12),
Align( SizedBox(
alignment: AlignmentDirectional.bottomEnd, height: 46,
child: Container(
width: geoPointDTO != null ? 220: 150,
height: 70,
child: RoundedButton( child: RoundedButton(
text: geoPointDTO != null ? "Sauvegarder" : "Créer", text:
geoPointDTO.id != null ? "Sauvegarder" : "Créer",
icon: Icons.check, icon: Icons.check,
color: kPrimaryColor, color: kPrimaryColor,
textColor: kWhite, textColor: kWhite,
press: () { press: () {
//print("TODO"); if (geoPointDTO.geometry != null &&
if (geoPointDTO.geometry != null && geoPointDTO.geometry!.coordinates != null) { geoPointDTO.geometry!.coordinates != null) {
getResult(geoPointDTO); getResult(geoPointDTO);
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
}, },
fontSize: 20, fontSize: 15,
), horizontal: 24,
), ),
), ),
], ],
), ),
], ],
), context: context ),
),
);
},
);
},
); );
} }

View File

@ -0,0 +1,156 @@
import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/Screens/Configurations/Section/SubSection/Parcours/showNewOrUpdateGuidedPath.dart';
class ParcoursConfig extends StatefulWidget {
final List<GuidedPathDTO> initialValue;
final String parentId;
final bool isEvent;
final bool isEscapeMode;
final ValueChanged<List<GuidedPathDTO>> onChanged;
const ParcoursConfig({
Key? key,
required this.initialValue,
required this.parentId,
required this.isEvent,
this.isEscapeMode = false,
required this.onChanged,
}) : super(key: key);
@override
_ParcoursConfigState createState() => _ParcoursConfigState();
}
class _ParcoursConfigState extends State<ParcoursConfig> {
late List<GuidedPathDTO> paths;
@override
void initState() {
super.initState();
paths = List.from(widget.initialValue);
paths.sort((a, b) => (a.order ?? 0).compareTo(b.order ?? 0));
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Parcours Guidés",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
ElevatedButton.icon(
icon: Icon(Icons.add),
label: Text("Ajouter un parcours"),
onPressed: () {
showNewOrUpdateGuidedPath(
context,
null,
widget.parentId,
widget.isEvent,
widget.isEscapeMode,
(newPath) {
setState(() {
newPath.order = paths.length;
paths.add(newPath);
widget.onChanged(paths);
});
},
);
},
style: ElevatedButton.styleFrom(
backgroundColor: kSuccess, foregroundColor: kWhite),
),
],
),
),
Expanded(
child: paths.isEmpty
? Center(
child: Text("Aucun parcours configuré",
style: TextStyle(fontStyle: FontStyle.italic)))
: ReorderableListView.builder(
buildDefaultDragHandles: false,
itemCount: paths.length,
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) newIndex -= 1;
final item = paths.removeAt(oldIndex);
paths.insert(newIndex, item);
for (int i = 0; i < paths.length; i++) {
paths[i].order = i;
}
widget.onChanged(paths);
});
},
itemBuilder: (context, index) {
final path = paths[index];
return Card(
key: ValueKey(path.id ?? index.toString()),
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
leading: CircleAvatar(
child: Text("${index + 1}"),
backgroundColor: kPrimaryColor,
foregroundColor: kWhite),
title: Text(path.title != null && path.title!.isNotEmpty
? path.title!
.firstWhere((t) => t.language == 'FR',
orElse: () => path.title![0])
.value ??
"Parcours sans titre"
: "Parcours sans titre"),
subtitle: Text("${path.steps?.length ?? 0} étapes"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit, color: kPrimaryColor),
onPressed: () {
showNewOrUpdateGuidedPath(
context,
path,
widget.parentId,
widget.isEvent,
widget.isEscapeMode,
(updatedPath) {
setState(() {
paths[index] = updatedPath;
widget.onChanged(paths);
});
},
);
},
),
IconButton(
icon: Icon(Icons.delete, color: kError),
onPressed: () {
setState(() {
paths.removeAt(index);
for (int i = 0; i < paths.length; i++) {
paths[i].order = i;
}
widget.onChanged(paths);
});
},
),
ReorderableDragStartListener(
index: index,
child: Icon(Icons.drag_handle),
),
],
),
),
);
},
),
),
],
);
}
}

View File

@ -0,0 +1,275 @@
import 'package:flutter/material.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,
Function(GuidedPathDTO) onSave,
) {
GuidedPathDTO workingPath = path != null
? GuidedPathDTO.fromJson(path.toJson())!
: GuidedPathDTO(
title: [],
description: [],
steps: [],
order: 0,
);
showDialog(
context: context,
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,
),
),
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,
),
),
],
),
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) {
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: Text(
step.title != null && step.title!.isNotEmpty
? step.title!
.firstWhere(
(t) => t.language == 'FR',
orElse: () =>
step.title![0])
.value ??
"Étape $index"
: "Étape $index",
),
subtitle: Text(
"${step.quizQuestions?.length ?? 0} question(s)"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit,
color: kPrimaryColor),
onPressed: () {
showNewOrUpdateGuidedStep(
context,
step,
workingPath.id ?? "temp",
isEscapeMode,
(updatedStep) {
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: "Sauvegarder",
press: () {
onSave(workingPath);
Navigator.pop(context);
},
color: kPrimaryColor,
fontSize: 15,
horizontal: 24,
),
),
],
),
],
),
),
);
},
);
},
);
}

View File

@ -0,0 +1,265 @@
import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/Components/confirmation_dialog.dart';
import 'package:manager_app/Components/geometry_input_container.dart';
import 'package:manager_app/Components/multi_string_input_container.dart';
import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/constants.dart';
import 'showNewOrUpdateQuizQuestion.dart';
void showNewOrUpdateGuidedStep(
BuildContext context,
GuidedStepDTO? step,
String pathId,
bool isEscapeMode,
Function(GuidedStepDTO) onSave,
) {
GuidedStepDTO workingStep = step != null
? GuidedStepDTO.fromJson(step.toJson())!
: GuidedStepDTO(
title: [],
description: [],
quizQuestions: [],
order: 0,
);
// Convert EventAddressDTOGeometry to GeometryDTO via JSON for the geometry picker
GeometryDTO? _toGeometryDTO(EventAddressDTOGeometry? geo) {
if (geo == null) return null;
return GeometryDTO.fromJson(geo.toJson());
}
EventAddressDTOGeometry? _toEventGeometry(GeometryDTO? geo) {
if (geo == null) return null;
return EventAddressDTOGeometry.fromJson(geo.toJson());
}
showDialog(
context: context,
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.75;
final double contentWidth = dialogWidth - 48;
final double halfWidth = (contentWidth - 20) / 2;
return Dialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
width: dialogWidth,
constraints: BoxConstraints(maxHeight: screenHeight * 0.85),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
step == null ? "Nouvelle Étape" : "Modifier l'Étape",
style: TextStyle(
color: kPrimaryColor,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(height: 16),
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 de l'étape",
initialValue: workingStep.title ?? [],
onGetResult: (val) =>
setState(() => workingStep.title = val),
maxLines: 1,
isTitle: true,
),
),
SizedBox(width: 20),
SizedBox(
width: halfWidth,
child: MultiStringInputContainer(
label: "Description :",
modalLabel: "Description de l'étape",
initialValue: workingStep.description ?? [],
onGetResult: (val) => setState(
() => workingStep.description = val),
maxLines: 1,
isTitle: false,
),
),
],
),
Divider(height: 24),
// Géométrie conversion JSON entre les deux types GeoDTO
GeometryInputContainer(
label: "Emplacement de l'étape :",
initialGeometry:
_toGeometryDTO(workingStep.geometry),
initialColor: null,
onSave: (geometry, color) {
setState(() {
workingStep.geometry =
_toEventGeometry(geometry);
});
},
),
// Questions uniquement en mode Escape Game
if (isEscapeMode) ...[
Divider(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Questions / Défis",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15)),
IconButton(
icon: Icon(Icons.add_circle_outline,
color: kSuccess),
onPressed: () {
showNewOrUpdateQuizQuestion(
context,
null,
workingStep.id ?? "temp",
isEscapeMode,
(newQuestion) {
setState(() {
workingStep.quizQuestions = [
...(workingStep.quizQuestions ??
[]),
newQuestion
];
});
},
);
},
),
],
),
if (workingStep.quizQuestions == null ||
workingStep.quizQuestions!.isEmpty)
Padding(
padding:
const EdgeInsets.symmetric(vertical: 8),
child: Text(
"Aucune question configurée.",
style: TextStyle(
fontStyle: FontStyle.italic,
color: Colors.grey[600]),
),
)
else
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: workingStep.quizQuestions!.length,
itemBuilder: (context, qIndex) {
final question =
workingStep.quizQuestions![qIndex];
return ListTile(
dense: true,
title: Text(question.label.isNotEmpty
? question.label
.firstWhere(
(t) => t.language == 'FR',
orElse: () =>
question.label[0])
.value ??
"Question $qIndex"
: "Question $qIndex"),
subtitle: Text(
"Type: ${question.validationQuestionType?.value == 2 ? 'Puzzle' : question.validationQuestionType?.value == 1 ? 'QCM' : 'Texte'}"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit,
size: 18, color: kPrimaryColor),
onPressed: () {
showNewOrUpdateQuizQuestion(
context,
question,
workingStep.id ?? "temp",
isEscapeMode,
(updatedQuestion) {
setState(() {
workingStep.quizQuestions![
qIndex] = updatedQuestion;
});
},
);
},
),
IconButton(
icon: Icon(Icons.delete,
size: 18, color: kError),
onPressed: () {
showConfirmationDialog(
"Supprimer cette question ?",
() {},
() => setState(() => workingStep
.quizQuestions!
.removeAt(qIndex)),
context,
);
},
),
],
),
);
},
),
],
],
),
),
),
SizedBox(height: 16),
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: "Sauvegarder",
press: () {
onSave(workingStep);
Navigator.pop(context);
},
color: kPrimaryColor,
fontSize: 15,
horizontal: 24,
),
),
],
),
],
),
),
);
},
);
},
);
}

View File

@ -0,0 +1,390 @@
import 'package:flutter/material.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/resource_input_container.dart';
import 'package:manager_app/Components/number_input_container.dart';
import 'package:manager_app/Components/check_input_container.dart';
// Conversions between TranslationDTO and TranslationAndResourceDTO
// (ResponseDTO.label uses TranslationAndResourceDTO, but we use MultiStringInputContainer
// which requires TranslationDTO the resource field is not needed for quiz responses)
List<TranslationDTO> _toTranslationList(
List<TranslationAndResourceDTO>? list) =>
(list ?? [])
.map((t) => TranslationDTO(language: t.language, value: t.value))
.toList();
List<TranslationAndResourceDTO> _fromTranslationList(
List<TranslationDTO> list) =>
list
.map((t) =>
TranslationAndResourceDTO(language: t.language, value: t.value))
.toList();
// Creates an empty ResponseDTO; labels are populated by MultiStringInputContainer
ResponseDTO _emptyResponse({bool isGood = false, int order = 0}) =>
ResponseDTO(label: [], isGood: isGood, order: order);
void showNewOrUpdateQuizQuestion(
BuildContext context,
QuizQuestion? question,
String stepId,
bool isEscapeMode,
Function(QuizQuestion) onSave,
) {
// QuizQuestion.label is List<TranslationAndResourceDTO> convert for display
List<TranslationDTO> workingLabel = _toTranslationList(
question != null && question.label.isNotEmpty
? question.label
: [TranslationAndResourceDTO(language: 'FR', value: '')]);
List<ResponseDTO> workingResponses =
question != null && question.responses.isNotEmpty
? question.responses
.map((r) => ResponseDTO.fromJson(r.toJson())!)
.toList()
: [];
QuizQuestion workingQuestion = QuizQuestion(
id: question?.id ?? 0,
label: _fromTranslationList(workingLabel), // kept in sync below
responses: workingResponses,
validationQuestionType:
question?.validationQuestionType ?? QuestionType.number0,
puzzleImageId: question?.puzzleImageId,
puzzleImage: question?.puzzleImage,
puzzleRows: question?.puzzleRows,
puzzleCols: question?.puzzleCols,
isSlidingPuzzle: question?.isSlidingPuzzle,
order: question?.order ?? 0,
guidedStepId: question?.guidedStepId ?? stepId,
);
showDialog(
context: context,
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.65;
final double contentWidth = dialogWidth - 48;
final double halfWidth = (contentWidth - 20) / 2;
void ensureSimpleResponse() {
if (workingQuestion.responses.isEmpty) {
workingQuestion.responses
.add(_emptyResponse(isGood: true, order: 0));
}
workingQuestion.responses[0].isGood = true;
}
return Dialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
width: dialogWidth,
constraints: BoxConstraints(maxHeight: screenHeight * 0.85),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
question == null
? "Nouvelle Question"
: "Modifier la Question",
style: TextStyle(
color: kPrimaryColor,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(height: 16),
Flexible(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- Intitulé (multi-langue via MultiStringInputContainer) ---
MultiStringInputContainer(
label: "Question posée :",
modalLabel: "Intitulé de la question",
initialValue: workingLabel,
onGetResult: (val) => setState(() {
workingLabel = val;
workingQuestion.label = _fromTranslationList(val);
}),
maxLines: 3,
isTitle: false,
),
SizedBox(height: 16),
// --- Type ---
Text("Type de validation :",
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 8),
DropdownButton<QuestionType>(
value: workingQuestion.validationQuestionType,
items: [
DropdownMenuItem(
value: QuestionType.number0,
child: Text("Simple (texte attendu)")),
DropdownMenuItem(
value: QuestionType.number1,
child: Text("Choix multiples (QCM)")),
DropdownMenuItem(
value: QuestionType.number2,
child: Text("Puzzle")),
],
onChanged: (val) => setState(() {
workingQuestion.validationQuestionType = val;
workingQuestion.responses = [];
}),
),
// =========================================
// Type 0 : Simple texte
// =========================================
if (workingQuestion.validationQuestionType ==
QuestionType.number0) ...[
Divider(height: 24),
Text("Réponse attendue :",
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Builder(builder: (_) {
ensureSimpleResponse();
final List<TranslationDTO> respLabel =
_toTranslationList(
workingQuestion.responses[0].label);
return MultiStringInputContainer(
label: "",
modalLabel: "Réponse attendue",
initialValue: respLabel,
onGetResult: (val) => setState(() {
workingQuestion.responses[0].label =
_fromTranslationList(val);
workingQuestion.responses[0].isGood = true;
}),
maxLines: 1,
isTitle: true,
);
}),
SizedBox(height: 4),
Text(
"La validation se fait par comparaison (insensible à la casse).",
style: TextStyle(
fontSize: 12,
fontStyle: FontStyle.italic,
color: Colors.grey[600]),
),
],
// =========================================
// Type 1 : QCM
// =========================================
if (workingQuestion.validationQuestionType ==
QuestionType.number1) ...[
Divider(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Réponses possibles :",
style:
TextStyle(fontWeight: FontWeight.bold)),
TextButton.icon(
icon: Icon(Icons.add_circle_outline,
color: kSuccess),
label: Text("Ajouter",
style: TextStyle(color: kSuccess)),
onPressed: () => setState(() =>
workingQuestion.responses.add(
_emptyResponse(
order: workingQuestion
.responses.length))),
),
],
),
if (workingQuestion.responses.isEmpty)
Padding(
padding:
const EdgeInsets.symmetric(vertical: 8),
child: Text(
"Aucune réponse définie. Ajoutez-en au moins une.",
style: TextStyle(
fontStyle: FontStyle.italic,
color: Colors.grey[600]),
),
)
else
Column(
children: List.generate(
workingQuestion.responses.length, (i) {
final resp = workingQuestion.responses[i];
final List<TranslationDTO> respLabel =
_toTranslationList(resp.label);
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
// Checkbox bonne réponse
Tooltip(
message: resp.isGood == true
? "Bonne réponse ✓"
: "Mauvaise réponse",
child: Checkbox(
value: resp.isGood ?? false,
activeColor: kSuccess,
onChanged: (val) => setState(
() => resp.isGood = val),
),
),
// Traductions (via MultiStringInputContainer)
Expanded(
child: MultiStringInputContainer(
label: "Réponse ${i + 1} :",
modalLabel: "Réponse ${i + 1}",
initialValue: respLabel,
onGetResult: (val) => setState(
() => resp.label =
_fromTranslationList(
val)),
maxLines: 1,
isTitle: true,
),
),
// Supprimer
IconButton(
icon: Icon(Icons.delete_outline,
color: kError, size: 20),
onPressed: () => setState(() =>
workingQuestion.responses
.removeAt(i)),
),
],
),
),
);
}),
),
SizedBox(height: 4),
Text(
"✓ = bonne réponse. Plusieurs peuvent être correctes.",
style: TextStyle(
fontSize: 12,
fontStyle: FontStyle.italic,
color: Colors.grey[600]),
),
],
// =========================================
// Type 2 : Puzzle
// =========================================
if (workingQuestion.validationQuestionType ==
QuestionType.number2) ...[
Divider(height: 24),
Text("Configuration du Puzzle",
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 12),
ResourceInputContainer(
label: "Image du puzzle :",
initialValue: workingQuestion.puzzleImageId,
onChanged: (res) => setState(() {
workingQuestion.puzzleImageId = res.id;
workingQuestion.puzzleImage =
Resource.fromJson(res.toJson());
}),
),
SizedBox(height: 12),
Row(
children: [
SizedBox(
width: halfWidth,
child: NumberInputContainer(
label: "Lignes :",
initialValue:
workingQuestion.puzzleRows ?? 3,
onChanged: (val) => setState(() =>
workingQuestion.puzzleRows =
int.tryParse(val) ?? 3),
isSmall: true,
),
),
SizedBox(width: 20),
SizedBox(
width: halfWidth,
child: NumberInputContainer(
label: "Colonnes :",
initialValue:
workingQuestion.puzzleCols ?? 3,
onChanged: (val) => setState(() =>
workingQuestion.puzzleCols =
int.tryParse(val) ?? 3),
isSmall: true,
),
),
],
),
SizedBox(height: 8),
CheckInputContainer(
label: "Puzzle glissant (Sliding) :",
isChecked:
workingQuestion.isSlidingPuzzle ?? false,
onChanged: (val) => setState(
() => workingQuestion.isSlidingPuzzle = val),
),
],
],
),
),
),
SizedBox(height: 16),
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: "Sauvegarder",
press: () {
for (int i = 0;
i < workingQuestion.responses.length;
i++) {
workingQuestion.responses[i].order = i;
}
onSave(workingQuestion);
Navigator.pop(context);
},
color: kPrimaryColor,
fontSize: 15,
horizontal: 24,
),
),
],
),
],
),
),
);
},
);
},
);
}

View File

@ -39,6 +39,7 @@ import 'package:pasteboard/pasteboard.dart';
import 'dart:html' as html; import 'dart:html' as html;
import 'SubSection/Weather/weather_config.dart'; import 'SubSection/Weather/weather_config.dart';
import 'SubSection/Event/event_config.dart';
class SectionDetailScreen extends StatefulWidget { class SectionDetailScreen extends StatefulWidget {
final String id; final String id;
@ -61,7 +62,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
Object? rawSectionData; Object? rawSectionData;
return FutureBuilder( return FutureBuilder(
future: getSection(widget.id, (appContext.getContext() as ManagerAppContext).clientAPI!), future: getSection(widget.id,
(appContext.getContext() as ManagerAppContext).clientAPI!),
builder: (context, AsyncSnapshot<dynamic> snapshot) { builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
rawSectionData = snapshot.data; rawSectionData = snapshot.data;
@ -72,7 +74,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
sectionDTO = nullableSection; sectionDTO = nullableSection;
return Stack( return Stack(
children: [ children: [
bodySection(rawSectionData, size, appContext, context, globalKey), bodySection(
rawSectionData, size, appContext, context, globalKey),
Align( Align(
alignment: AlignmentDirectional.bottomCenter, alignment: AlignmentDirectional.bottomCenter,
child: Container( child: Container(
@ -83,23 +86,22 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
], ],
); );
} else { } else {
return Center(child: Text("Une erreur est survenue lors de la récupération de la section")); return Center(
child: Text(
"Une erreur est survenue lors de la récupération de la section"));
} }
} else if (snapshot.connectionState == ConnectionState.none) { } else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data"); return Text("No data");
} else { } else {
return Center( return Center(
child: Container( child: Container(
height: size.height * 0.2, height: size.height * 0.2, child: CommonLoader()));
child: CommonLoader()
)
);
} }
} });
);
} }
Widget bodySection(Object? rawSectionDTO, Size size, AppContext appContext, BuildContext context, GlobalKey globalKey) { Widget bodySection(Object? rawSectionDTO, Size size, AppContext appContext,
BuildContext context, GlobalKey globalKey) {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
//SectionDTO? sectionDTO = SectionDTO.fromJson(rawSectionDTO); //SectionDTO? sectionDTO = SectionDTO.fromJson(rawSectionDTO);
@ -131,26 +133,33 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
size: 25, size: 25,
), ),
), ),
Text(sectionDTO.label!, style: TextStyle(fontSize: 30, fontWeight: FontWeight.w400)), Text(sectionDTO.label!,
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.w400)),
//if((appContext.getContext() as ManagerAppContext).selectedConfiguration!.isMobile!) //if((appContext.getContext() as ManagerAppContext).selectedConfiguration!.isMobile!)
DownloadPDF(sections: [sectionDTO]), DownloadPDF(sections: [sectionDTO]),
], ],
), ),
Padding( Padding(
padding: const EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
child: Text(DateFormat('dd/MM/yyyy').format(sectionDTO.dateCreation!), style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200)), child: Text(
DateFormat('dd/MM/yyyy')
.format(sectionDTO.dateCreation!),
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w200)),
), ),
], ],
), ),
) )),
),
Padding( Padding(
padding: const EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
child: Align( child: Align(
alignment: AlignmentDirectional.centerEnd, alignment: AlignmentDirectional.centerEnd,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext =
appContext.getContext();
managerAppContext.selectedSection = null; managerAppContext.selectedSection = null;
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
}, },
@ -159,9 +168,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
Icons.arrow_back, Icons.arrow_back,
color: kPrimaryColor, color: kPrimaryColor,
size: 50.0, size: 50.0,
) ))),
)
),
), ),
) )
], ],
@ -190,9 +197,15 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
children: [ children: [
InkWell( InkWell(
onTap: () async { onTap: () async {
var image = await _captureAndSharePng(globalKey, sectionDTO.id!); var image = await _captureAndSharePng(
globalKey, sectionDTO.id!);
await readAndWriteFiles(image); await readAndWriteFiles(image);
showNotification(kSuccess, kWhite, 'Ce QR code a été copié dans le presse papier', context, null); showNotification(
kSuccess,
kWhite,
'Ce QR code a été copié dans le presse papier',
context,
null);
}, },
child: Container( child: Container(
width: size.width * 0.1, width: size.width * 0.1,
@ -200,15 +213,21 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
child: RepaintBoundary( child: RepaintBoundary(
key: globalKey, key: globalKey,
child: QrImageView( child: QrImageView(
padding: EdgeInsets.only(left: 5.0, top: 5.0, bottom: 5.0, right: 5.0), padding: EdgeInsets.only(
data: "${managerAppContext.host!.replaceFirst("api", "web")}/${managerAppContext.instanceId}/${managerAppContext.selectedConfiguration!.id}/${sectionDTO.id!}", left: 5.0,
top: 5.0,
bottom: 5.0,
right: 5.0),
data:
"${managerAppContext.host!.replaceFirst("api", "web")}/${managerAppContext.instanceId}/${managerAppContext.selectedConfiguration!.id}/${sectionDTO.id!}",
version: QrVersions.auto, version: QrVersions.auto,
size: 50.0, size: 50.0,
), ),
), ),
), ),
), ),
SelectableText(sectionDTO.id!, style: new TextStyle(fontSize: 15)) SelectableText(sectionDTO.id!,
style: new TextStyle(fontSize: 15))
], ],
), ),
CheckInputContainer( CheckInputContainer(
@ -224,14 +243,21 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
if (sectionDTO.isBeacon!) if (sectionDTO.isBeacon!)
NumberInputContainer( NumberInputContainer(
label: "Identifiant Beacon :", label: "Identifiant Beacon :",
initialValue: sectionDTO.beaconId != null ? sectionDTO.beaconId! : 0, initialValue: sectionDTO.beaconId != null
? sectionDTO.beaconId!
: 0,
isSmall: true, isSmall: true,
onChanged: (value) { onChanged: (value) {
try { try {
sectionDTO.beaconId = int.parse(value); sectionDTO.beaconId = int.parse(value);
} catch (e) { } catch (e) {
print('BeaconId not a number'); print('BeaconId not a number');
showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null); showNotification(
Colors.orange,
kWhite,
'Cela doit être un chiffre',
context,
null);
} }
}, },
), ),
@ -326,8 +352,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
decoration: BoxDecoration( decoration: BoxDecoration(
//color: Colors.lightGreen, //color: Colors.lightGreen,
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
border: Border.all(width: 1.5, color: kSecond) border: Border.all(width: 1.5, color: kSecond)),
),
), ),
], ],
), ),
@ -386,10 +411,12 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
Future<void> cancel(AppContext appContext) async { Future<void> cancel(AppContext appContext) async {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
Object? rawData = await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionApi!.sectionGetDetail(sectionDTO.id!); Object? rawData = await (appContext.getContext() as ManagerAppContext)
.clientAPI!
.sectionApi!
.sectionGetDetail(sectionDTO.id!);
var nullableSection = SectionDTO.fromJson(rawData); var nullableSection = SectionDTO.fromJson(rawData);
if(nullableSection != null) if (nullableSection != null) {
{
managerAppContext.selectedSection = nullableSection!; managerAppContext.selectedSection = nullableSection!;
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
} }
@ -397,22 +424,22 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
Future<void> delete(AppContext appContext) async { Future<void> delete(AppContext appContext) async {
showConfirmationDialog( showConfirmationDialog(
"Êtes-vous sûr de vouloir supprimer cette section ?", "Êtes-vous sûr de vouloir supprimer cette section ?", () {}, () async {
() {},
() async {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
await managerAppContext.clientAPI!.sectionApi!.sectionDelete(sectionDTO.id!); await managerAppContext.clientAPI!.sectionApi!
.sectionDelete(sectionDTO.id!);
managerAppContext.selectedSection = null; managerAppContext.selectedSection = null;
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
}, }, context);
context
);
} }
Future<void> save(bool isTraduction, AppContext appContext) async { Future<void> save(bool isTraduction, AppContext appContext) async {
updateSectionDetail(); updateSectionDetail();
var sectionResult = await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionApi!.sectionUpdate(sectionDetailDTO); var sectionResult = await (appContext.getContext() as ManagerAppContext)
.clientAPI!
.sectionApi!
.sectionUpdate(sectionDetailDTO);
SectionDTO? section = SectionDTO.fromJson(sectionResult); SectionDTO? section = SectionDTO.fromJson(sectionResult);
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
@ -420,9 +447,15 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
if (isTraduction) { if (isTraduction) {
showNotification(kSuccess, kWhite, 'Les traductions de la section ont été sauvegardées avec succès', context, null); showNotification(
kSuccess,
kWhite,
'Les traductions de la section ont été sauvegardées avec succès',
context,
null);
} else { } else {
showNotification(kSuccess, kWhite, 'La section a été sauvegardée avec succès', context, null); showNotification(kSuccess, kWhite,
'La section a été sauvegardée avec succès', context, null);
} }
} }
@ -529,19 +562,28 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
sectionDetailDTO = updatedWeather; sectionDetailDTO = updatedWeather;
}, },
); );
case SectionType.Event:
SectionEventDTO eventDTO = SectionEventDTO.fromJson(rawSectionData)!;
sectionDetailDTO = eventDTO;
return EventConfig(
initialValue: eventDTO,
onChanged: (SectionEventDTO updatedEvent) {
sectionDetailDTO = updatedEvent;
},
);
} }
} }
updateSectionDetail() { updateSectionDetail() {
switch(sectionDTO.type) switch (sectionDTO.type) {
{
case SectionType.Map: case SectionType.Map:
(sectionDetailDTO as MapDTO).id = sectionDTO.id; (sectionDetailDTO as MapDTO).id = sectionDTO.id;
(sectionDetailDTO as MapDTO).order = sectionDTO.order; (sectionDetailDTO as MapDTO).order = sectionDTO.order;
(sectionDetailDTO as MapDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as MapDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as MapDTO).type = sectionDTO.type; (sectionDetailDTO as MapDTO).type = sectionDTO.type;
(sectionDetailDTO as MapDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as MapDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as MapDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as MapDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as MapDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as MapDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as MapDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as MapDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as MapDTO).label = sectionDTO.label; (sectionDetailDTO as MapDTO).label = sectionDTO.label;
@ -561,7 +603,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as SliderDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as SliderDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as SliderDTO).type = sectionDTO.type; (sectionDetailDTO as SliderDTO).type = sectionDTO.type;
(sectionDetailDTO as SliderDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as SliderDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as SliderDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as SliderDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as SliderDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as SliderDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as SliderDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as SliderDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as SliderDTO).label = sectionDTO.label; (sectionDetailDTO as SliderDTO).label = sectionDTO.label;
@ -581,7 +624,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as VideoDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as VideoDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as VideoDTO).type = sectionDTO.type; (sectionDetailDTO as VideoDTO).type = sectionDTO.type;
(sectionDetailDTO as VideoDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as VideoDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as VideoDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as VideoDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as VideoDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as VideoDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as VideoDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as VideoDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as VideoDTO).label = sectionDTO.label; (sectionDetailDTO as VideoDTO).label = sectionDTO.label;
@ -601,7 +645,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as WebDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as WebDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as WebDTO).type = sectionDTO.type; (sectionDetailDTO as WebDTO).type = sectionDTO.type;
(sectionDetailDTO as WebDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as WebDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as WebDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as WebDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as WebDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as WebDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as WebDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as WebDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as WebDTO).label = sectionDTO.label; (sectionDetailDTO as WebDTO).label = sectionDTO.label;
@ -621,7 +666,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as MenuDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as MenuDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as MenuDTO).type = sectionDTO.type; (sectionDetailDTO as MenuDTO).type = sectionDTO.type;
(sectionDetailDTO as MenuDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as MenuDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as MenuDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as MenuDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as MenuDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as MenuDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as MenuDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as MenuDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as MenuDTO).label = sectionDTO.label; (sectionDetailDTO as MenuDTO).label = sectionDTO.label;
@ -641,7 +687,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as QuizDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as QuizDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as QuizDTO).type = sectionDTO.type; (sectionDetailDTO as QuizDTO).type = sectionDTO.type;
(sectionDetailDTO as QuizDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as QuizDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as QuizDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as QuizDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as QuizDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as QuizDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as QuizDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as QuizDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as QuizDTO).label = sectionDTO.label; (sectionDetailDTO as QuizDTO).label = sectionDTO.label;
@ -661,7 +708,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as ArticleDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as ArticleDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as ArticleDTO).type = sectionDTO.type; (sectionDetailDTO as ArticleDTO).type = sectionDTO.type;
(sectionDetailDTO as ArticleDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as ArticleDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as ArticleDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as ArticleDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as ArticleDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as ArticleDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as ArticleDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as ArticleDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as ArticleDTO).label = sectionDTO.label; (sectionDetailDTO as ArticleDTO).label = sectionDTO.label;
@ -681,7 +729,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as PdfDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as PdfDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as PdfDTO).type = sectionDTO.type; (sectionDetailDTO as PdfDTO).type = sectionDTO.type;
(sectionDetailDTO as PdfDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as PdfDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as PdfDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as PdfDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as PdfDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as PdfDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as PdfDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as PdfDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as PdfDTO).label = sectionDTO.label; (sectionDetailDTO as PdfDTO).label = sectionDTO.label;
@ -701,7 +750,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as GameDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as GameDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as GameDTO).type = sectionDTO.type; (sectionDetailDTO as GameDTO).type = sectionDTO.type;
(sectionDetailDTO as GameDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as GameDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as GameDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as GameDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as GameDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as GameDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as GameDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as GameDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as GameDTO).label = sectionDTO.label; (sectionDetailDTO as GameDTO).label = sectionDTO.label;
@ -721,7 +771,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as AgendaDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as AgendaDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as AgendaDTO).type = sectionDTO.type; (sectionDetailDTO as AgendaDTO).type = sectionDTO.type;
(sectionDetailDTO as AgendaDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as AgendaDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as AgendaDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as AgendaDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as AgendaDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as AgendaDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as AgendaDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as AgendaDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as AgendaDTO).label = sectionDTO.label; (sectionDetailDTO as AgendaDTO).label = sectionDTO.label;
@ -741,7 +792,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as WeatherDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as WeatherDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as WeatherDTO).type = sectionDTO.type; (sectionDetailDTO as WeatherDTO).type = sectionDTO.type;
(sectionDetailDTO as WeatherDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as WeatherDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as WeatherDTO).configurationId = sectionDTO.configurationId; (sectionDetailDTO as WeatherDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as WeatherDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as WeatherDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as WeatherDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as WeatherDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as WeatherDTO).label = sectionDTO.label; (sectionDetailDTO as WeatherDTO).label = sectionDTO.label;
@ -755,6 +807,33 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as WeatherDTO).longitude = sectionDTO.longitude; (sectionDetailDTO as WeatherDTO).longitude = sectionDTO.longitude;
(sectionDetailDTO as WeatherDTO).meterZoneGPS = sectionDTO.meterZoneGPS; (sectionDetailDTO as WeatherDTO).meterZoneGPS = sectionDTO.meterZoneGPS;
break; break;
case SectionType.Event:
(sectionDetailDTO as SectionEventDTO).id = sectionDTO.id;
(sectionDetailDTO as SectionEventDTO).order = sectionDTO.order;
(sectionDetailDTO as SectionEventDTO).dateCreation =
sectionDTO.dateCreation;
(sectionDetailDTO as SectionEventDTO).type = sectionDTO.type;
(sectionDetailDTO as SectionEventDTO).instanceId =
sectionDTO.instanceId;
(sectionDetailDTO as SectionEventDTO).configurationId =
sectionDTO.configurationId;
(sectionDetailDTO as SectionEventDTO).isSubSection =
sectionDTO.isSubSection;
(sectionDetailDTO as SectionEventDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as SectionEventDTO).label = sectionDTO.label;
(sectionDetailDTO as SectionEventDTO).title = sectionDTO.title;
(sectionDetailDTO as SectionEventDTO).description =
sectionDTO.description;
(sectionDetailDTO as SectionEventDTO).imageId = sectionDTO.imageId;
(sectionDetailDTO as SectionEventDTO).imageSource =
sectionDTO.imageSource;
(sectionDetailDTO as SectionEventDTO).isBeacon = sectionDTO.isBeacon;
(sectionDetailDTO as SectionEventDTO).beaconId = sectionDTO.beaconId;
(sectionDetailDTO as SectionEventDTO).latitude = sectionDTO.latitude;
(sectionDetailDTO as SectionEventDTO).longitude = sectionDTO.longitude;
(sectionDetailDTO as SectionEventDTO).meterZoneGPS =
sectionDTO.meterZoneGPS;
break;
} }
} }
} }
@ -763,16 +842,17 @@ Future<Object?> getSection(String sectionId, Client client) async {
try { try {
Object? section = await client.sectionApi!.sectionGetDetail(sectionId); Object? section = await client.sectionApi!.sectionGetDetail(sectionId);
return section; return section;
} catch (e) { } catch (e) {
print(e); print(e);
return null; return null;
} }
} }
Future<Uint8List?> _captureAndSharePng(GlobalKey globalKey, String sectionId) async { Future<Uint8List?> _captureAndSharePng(
GlobalKey globalKey, String sectionId) async {
try { try {
RenderRepaintBoundary ? boundary = globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary; RenderRepaintBoundary? boundary =
globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
var image = await boundary.toImage(); var image = await boundary.toImage();
ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List(); Uint8List pngBytes = byteData!.buffer.asUint8List();
@ -782,7 +862,6 @@ Future<Uint8List?> _captureAndSharePng(GlobalKey globalKey, String sectionId) as
a.click(); a.click();
return pngBytes; return pngBytes;
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
return null; return null;

View File

@ -8,20 +8,23 @@ import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart'; import 'package:manager_app/constants.dart';
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext, BuildContext contextBuild, bool isSubSection) { Future<SectionDTO?> showNewSection(String configurationId,
AppContext appContext, BuildContext contextBuild, bool isSubSection) {
SectionDTO sectionDTO = new SectionDTO(); SectionDTO sectionDTO = new SectionDTO();
sectionDTO.label = ""; sectionDTO.label = "";
sectionDTO.configurationId = configurationId; sectionDTO.configurationId = configurationId;
sectionDTO.isSubSection = isSubSection; sectionDTO.isSubSection = isSubSection;
sectionDTO.parentId = isSubSection ? (appContext.getContext() as ManagerAppContext).selectedSection!.id : null; sectionDTO.isActive = true;
sectionDTO.parentId = isSubSection
? (appContext.getContext() as ManagerAppContext).selectedSection!.id
: null;
Size size = MediaQuery.of(contextBuild).size; Size size = MediaQuery.of(contextBuild).size;
sectionDTO.type = SectionType.Map; sectionDTO.type = SectionType.Map;
var section = showDialog<SectionDTO?>( var section = showDialog<SectionDTO?>(
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => AlertDialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))),
),
content: SingleChildScrollView( content: SingleChildScrollView(
child: SizedBox( child: SizedBox(
width: 750, width: 750,
@ -30,7 +33,12 @@ Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext
constraints: BoxConstraints(minHeight: 300, minWidth: 300), constraints: BoxConstraints(minHeight: 300, minWidth: 300),
child: Column( child: Column(
children: [ children: [
Text(isSubSection? "Nouvelle sous section": "Nouvelle section", style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)), Text(
isSubSection
? "Nouvelle sous section"
: "Nouvelle section",
style: new TextStyle(
fontSize: 25, fontWeight: FontWeight.w400)),
Column( Column(
children: [ children: [
SizedBox( SizedBox(
@ -45,7 +53,13 @@ Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext
), ),
DropDownInputContainer( DropDownInputContainer(
label: "Type:", label: "Type:",
values: isSubSection ? section_types.where((sectionType) => sectionType != "Menu").toList(): section_types.toList(), // Todo get menu by enum type values: isSubSection
? section_types
.where(
(sectionType) => sectionType != "Menu")
.toList()
: section_types
.toList(), // Todo get menu by enum type
initialValue: "Map", initialValue: "Map",
onChange: (String? value) { onChange: (String? value) {
sectionDTO.type = SectionType.fromJson(value); sectionDTO.type = SectionType.fromJson(value);
@ -99,12 +113,18 @@ Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext
color: kPrimaryColor, color: kPrimaryColor,
textColor: kWhite, textColor: kWhite,
press: () { press: () {
if(sectionDTO.label != null && sectionDTO.label!.length > 2) { if (sectionDTO.label != null &&
sectionDTO.label!.length > 2) {
//onYes(); //onYes();
Navigator.of(context).pop(sectionDTO); Navigator.of(context).pop(sectionDTO);
//create(sectionDTO, appContext, context, isSubSection, sendSubSection); //create(sectionDTO, appContext, context, isSubSection, sendSubSection);
} else { } else {
showNotification(Colors.orange, kWhite, 'Veuillez spécifier un nom pour la nouvelle section', context, null); showNotification(
Colors.orange,
kWhite,
'Veuillez spécifier un nom pour la nouvelle section',
context,
null);
} }
}, },
fontSize: 20, fontSize: 20,
@ -114,19 +134,21 @@ Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext
], ],
), ),
], ],
), context: contextBuild ),
); context: contextBuild);
return section; return section;
} }
void create(SectionDTO sectionDTO, AppContext appContext, BuildContext context, bool isSubSection, Function? sendSubSection) async { void create(SectionDTO sectionDTO, AppContext appContext, BuildContext context,
bool isSubSection, Function? sendSubSection) async {
if (sectionDTO.label != null) { if (sectionDTO.label != null) {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
sectionDTO.instanceId = managerAppContext.instanceId; sectionDTO.instanceId = managerAppContext.instanceId;
sectionDTO.isBeacon = false; sectionDTO.isBeacon = false;
sectionDTO.dateCreation = DateTime.now(); sectionDTO.dateCreation = DateTime.now();
SectionDTO? newSection = await managerAppContext.clientAPI!.sectionApi!.sectionCreate(sectionDTO); SectionDTO? newSection = await managerAppContext.clientAPI!.sectionApi!
.sectionCreate(sectionDTO);
if (!isSubSection) { if (!isSubSection) {
/*if (managerAppContext.selectedConfiguration.sectionIds == null) { /*if (managerAppContext.selectedConfiguration.sectionIds == null) {
@ -134,7 +156,8 @@ void create(SectionDTO sectionDTO, AppContext appContext, BuildContext context,
} }
managerAppContext.selectedConfiguration.sectionIds.add(newSection.id);*/ managerAppContext.selectedConfiguration.sectionIds.add(newSection.id);*/
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
showNotification(kSuccess, kWhite, 'La section a été créée avec succès !', context, null); showNotification(kSuccess, kWhite, 'La section a été créée avec succès !',
context, null);
} else { } else {
sendSubSection!(newSection); sendSubSection!(newSection);
} }

View File

@ -39,6 +39,7 @@ class GameDTO {
this.rows, this.rows,
this.cols, this.cols,
this.gameType, this.gameType,
this.guidedPaths = const [],
}); });
String? id; String? id;
@ -135,6 +136,8 @@ class GameDTO {
/// ///
GameTypes? gameType; GameTypes? gameType;
List<GuidedPathDTO>? guidedPaths;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
@ -164,7 +167,8 @@ class GameDTO {
other.puzzleImageId == puzzleImageId && other.puzzleImageId == puzzleImageId &&
other.rows == rows && other.rows == rows &&
other.cols == cols && other.cols == cols &&
other.gameType == gameType; other.gameType == gameType &&
_deepEquality.equals(other.guidedPaths, guidedPaths);
@override @override
int get hashCode => int get hashCode =>
@ -194,11 +198,12 @@ class GameDTO {
(puzzleImageId == null ? 0 : puzzleImageId!.hashCode) + (puzzleImageId == null ? 0 : puzzleImageId!.hashCode) +
(rows == null ? 0 : rows!.hashCode) + (rows == null ? 0 : rows!.hashCode) +
(cols == null ? 0 : cols!.hashCode) + (cols == null ? 0 : cols!.hashCode) +
(gameType == null ? 0 : gameType!.hashCode); (gameType == null ? 0 : gameType!.hashCode) +
(guidedPaths == null ? 0 : guidedPaths!.hashCode);
@override @override
String toString() => String toString() =>
'GameDTO[id=$id, label=$label, title=$title, description=$description, isActive=$isActive, imageId=$imageId, imageSource=$imageSource, configurationId=$configurationId, isSubSection=$isSubSection, parentId=$parentId, type=$type, dateCreation=$dateCreation, order=$order, instanceId=$instanceId, latitude=$latitude, longitude=$longitude, meterZoneGPS=$meterZoneGPS, isBeacon=$isBeacon, beaconId=$beaconId, messageDebut=$messageDebut, messageFin=$messageFin, puzzleImage=$puzzleImage, puzzleImageId=$puzzleImageId, rows=$rows, cols=$cols, gameType=$gameType]'; 'GameDTO[id=$id, label=$label, title=$title, description=$description, isActive=$isActive, imageId=$imageId, imageSource=$imageSource, configurationId=$configurationId, isSubSection=$isSubSection, parentId=$parentId, type=$type, dateCreation=$dateCreation, order=$order, instanceId=$instanceId, latitude=$latitude, longitude=$longitude, meterZoneGPS=$meterZoneGPS, isBeacon=$isBeacon, beaconId=$beaconId, messageDebut=$messageDebut, messageFin=$messageFin, puzzleImage=$puzzleImage, puzzleImageId=$puzzleImageId, rows=$rows, cols=$cols, gameType=$gameType, guidedPaths=$guidedPaths]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -213,12 +218,12 @@ class GameDTO {
json[r'label'] = null; json[r'label'] = null;
} }
if (this.title != null) { if (this.title != null) {
json[r'title'] = this.title; json[r'title'] = this.title!.map((v) => v.toJson()).toList();
} else { } else {
json[r'title'] = null; json[r'title'] = null;
} }
if (this.description != null) { if (this.description != null) {
json[r'description'] = this.description; json[r'description'] = this.description!.map((v) => v.toJson()).toList();
} else { } else {
json[r'description'] = null; json[r'description'] = null;
} }
@ -298,12 +303,13 @@ class GameDTO {
json[r'beaconId'] = null; json[r'beaconId'] = null;
} }
if (this.messageDebut != null) { if (this.messageDebut != null) {
json[r'messageDebut'] = this.messageDebut; json[r'messageDebut'] =
this.messageDebut!.map((v) => v.toJson()).toList();
} else { } else {
json[r'messageDebut'] = null; json[r'messageDebut'] = null;
} }
if (this.messageFin != null) { if (this.messageFin != null) {
json[r'messageFin'] = this.messageFin; json[r'messageFin'] = this.messageFin!.map((v) => v.toJson()).toList();
} else { } else {
json[r'messageFin'] = null; json[r'messageFin'] = null;
} }
@ -332,6 +338,11 @@ class GameDTO {
} else { } else {
json[r'gameType'] = null; json[r'gameType'] = null;
} }
if (this.guidedPaths != null) {
json[r'guidedPaths'] = this.guidedPaths!.map((v) => v.toJson()).toList();
} else {
json[r'guidedPaths'] = null;
}
return json; return json;
} }
@ -383,6 +394,7 @@ class GameDTO {
rows: mapValueOfType<int>(json, r'rows'), rows: mapValueOfType<int>(json, r'rows'),
cols: mapValueOfType<int>(json, r'cols'), cols: mapValueOfType<int>(json, r'cols'),
gameType: GameTypes.fromJson(json[r'gameType']), gameType: GameTypes.fromJson(json[r'gameType']),
guidedPaths: GuidedPathDTO.listFromJson(json[r'guidedPaths']),
); );
} }
return null; return null;

View File

@ -19,6 +19,7 @@ class GuidedPathDTO {
this.description = const [], this.description = const [],
this.sectionMapId, this.sectionMapId,
this.sectionEventId, this.sectionEventId,
this.sectionGameId,
this.isLinear, this.isLinear,
this.requireSuccessToAdvance, this.requireSuccessToAdvance,
this.hideNextStepsUntilComplete, this.hideNextStepsUntilComplete,
@ -38,6 +39,8 @@ class GuidedPathDTO {
String? sectionEventId; String? sectionEventId;
String? sectionGameId;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated /// does not include a default value (using the "default:" property), however, the generated
@ -82,6 +85,7 @@ class GuidedPathDTO {
_deepEquality.equals(other.description, description) && _deepEquality.equals(other.description, description) &&
other.sectionMapId == sectionMapId && other.sectionMapId == sectionMapId &&
other.sectionEventId == sectionEventId && other.sectionEventId == sectionEventId &&
other.sectionGameId == sectionGameId &&
other.isLinear == isLinear && other.isLinear == isLinear &&
other.requireSuccessToAdvance == requireSuccessToAdvance && other.requireSuccessToAdvance == requireSuccessToAdvance &&
other.hideNextStepsUntilComplete == hideNextStepsUntilComplete && other.hideNextStepsUntilComplete == hideNextStepsUntilComplete &&
@ -97,6 +101,7 @@ class GuidedPathDTO {
(description == null ? 0 : description!.hashCode) + (description == null ? 0 : description!.hashCode) +
(sectionMapId == null ? 0 : sectionMapId!.hashCode) + (sectionMapId == null ? 0 : sectionMapId!.hashCode) +
(sectionEventId == null ? 0 : sectionEventId!.hashCode) + (sectionEventId == null ? 0 : sectionEventId!.hashCode) +
(sectionGameId == null ? 0 : sectionGameId!.hashCode) +
(isLinear == null ? 0 : isLinear!.hashCode) + (isLinear == null ? 0 : isLinear!.hashCode) +
(requireSuccessToAdvance == null (requireSuccessToAdvance == null
? 0 ? 0
@ -109,7 +114,7 @@ class GuidedPathDTO {
@override @override
String toString() => String toString() =>
'GuidedPathDTO[id=$id, instanceId=$instanceId, title=$title, description=$description, sectionMapId=$sectionMapId, sectionEventId=$sectionEventId, isLinear=$isLinear, requireSuccessToAdvance=$requireSuccessToAdvance, hideNextStepsUntilComplete=$hideNextStepsUntilComplete, order=$order, steps=$steps]'; 'GuidedPathDTO[id=$id, instanceId=$instanceId, title=$title, description=$description, sectionMapId=$sectionMapId, sectionEventId=$sectionEventId, sectionGameId=$sectionGameId, isLinear=$isLinear, requireSuccessToAdvance=$requireSuccessToAdvance, hideNextStepsUntilComplete=$hideNextStepsUntilComplete, order=$order, steps=$steps]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -124,12 +129,12 @@ class GuidedPathDTO {
json[r'instanceId'] = null; json[r'instanceId'] = null;
} }
if (this.title != null) { if (this.title != null) {
json[r'title'] = this.title; json[r'title'] = this.title!.map((v) => v.toJson()).toList();
} else { } else {
json[r'title'] = null; json[r'title'] = null;
} }
if (this.description != null) { if (this.description != null) {
json[r'description'] = this.description; json[r'description'] = this.description!.map((v) => v.toJson()).toList();
} else { } else {
json[r'description'] = null; json[r'description'] = null;
} }
@ -143,6 +148,11 @@ class GuidedPathDTO {
} else { } else {
json[r'sectionEventId'] = null; json[r'sectionEventId'] = null;
} }
if (this.sectionGameId != null) {
json[r'sectionGameId'] = this.sectionGameId;
} else {
json[r'sectionGameId'] = null;
}
if (this.isLinear != null) { if (this.isLinear != null) {
json[r'isLinear'] = this.isLinear; json[r'isLinear'] = this.isLinear;
} else { } else {
@ -164,7 +174,7 @@ class GuidedPathDTO {
json[r'order'] = null; json[r'order'] = null;
} }
if (this.steps != null) { if (this.steps != null) {
json[r'steps'] = this.steps; json[r'steps'] = this.steps!.map((v) => v.toJson()).toList();
} else { } else {
json[r'steps'] = null; json[r'steps'] = null;
} }
@ -198,6 +208,7 @@ class GuidedPathDTO {
description: TranslationDTO.listFromJson(json[r'description']), description: TranslationDTO.listFromJson(json[r'description']),
sectionMapId: mapValueOfType<String>(json, r'sectionMapId'), sectionMapId: mapValueOfType<String>(json, r'sectionMapId'),
sectionEventId: mapValueOfType<String>(json, r'sectionEventId'), sectionEventId: mapValueOfType<String>(json, r'sectionEventId'),
sectionGameId: mapValueOfType<String>(json, r'sectionGameId'),
isLinear: mapValueOfType<bool>(json, r'isLinear'), isLinear: mapValueOfType<bool>(json, r'isLinear'),
requireSuccessToAdvance: requireSuccessToAdvance:
mapValueOfType<bool>(json, r'requireSuccessToAdvance'), mapValueOfType<bool>(json, r'requireSuccessToAdvance'),

View File

@ -47,7 +47,7 @@ class GuidedStepDTO {
List<TranslationDTO>? description; List<TranslationDTO>? description;
EventAddressDTOGeometry? geometry; GeometryDTO? geometry;
double? zoneRadiusMeters; double? zoneRadiusMeters;
@ -151,12 +151,12 @@ class GuidedStepDTO {
json[r'order'] = null; json[r'order'] = null;
} }
if (this.title != null) { if (this.title != null) {
json[r'title'] = this.title; json[r'title'] = this.title!.map((v) => v.toJson()).toList();
} else { } else {
json[r'title'] = null; json[r'title'] = null;
} }
if (this.description != null) { if (this.description != null) {
json[r'description'] = this.description; json[r'description'] = this.description!.map((v) => v.toJson()).toList();
} else { } else {
json[r'description'] = null; json[r'description'] = null;
} }
@ -206,12 +206,14 @@ class GuidedStepDTO {
json[r'timerSeconds'] = null; json[r'timerSeconds'] = null;
} }
if (this.timerExpiredMessage != null) { if (this.timerExpiredMessage != null) {
json[r'timerExpiredMessage'] = this.timerExpiredMessage; json[r'timerExpiredMessage'] =
this.timerExpiredMessage!.map((v) => v.toJson()).toList();
} else { } else {
json[r'timerExpiredMessage'] = null; json[r'timerExpiredMessage'] = null;
} }
if (this.quizQuestions != null) { if (this.quizQuestions != null) {
json[r'quizQuestions'] = this.quizQuestions; json[r'quizQuestions'] =
this.quizQuestions!.map((v) => v.toJson()).toList();
} else { } else {
json[r'quizQuestions'] = null; json[r'quizQuestions'] = null;
} }
@ -244,7 +246,7 @@ class GuidedStepDTO {
order: mapValueOfType<int>(json, r'order'), order: mapValueOfType<int>(json, r'order'),
title: TranslationDTO.listFromJson(json[r'title']), title: TranslationDTO.listFromJson(json[r'title']),
description: TranslationDTO.listFromJson(json[r'description']), description: TranslationDTO.listFromJson(json[r'description']),
geometry: EventAddressDTOGeometry.fromJson(json[r'geometry']), geometry: GeometryDTO.fromJson(json[r'geometry']),
zoneRadiusMeters: mapValueOfType<double>(json, r'zoneRadiusMeters'), zoneRadiusMeters: mapValueOfType<double>(json, r'zoneRadiusMeters'),
imageUrl: mapValueOfType<String>(json, r'imageUrl'), imageUrl: mapValueOfType<String>(json, r'imageUrl'),
triggerGeoPointId: mapValueOfType<int>(json, r'triggerGeoPointId'), triggerGeoPointId: mapValueOfType<int>(json, r'triggerGeoPointId'),

View File

@ -42,6 +42,8 @@ class MapDTO {
this.categories = const [], this.categories = const [],
this.centerLatitude, this.centerLatitude,
this.centerLongitude, this.centerLongitude,
this.isParcours,
this.guidedPaths = const [],
}); });
String? id; String? id;
@ -132,6 +134,10 @@ class MapDTO {
String? centerLongitude; String? centerLongitude;
bool? isParcours;
List<GuidedPathDTO>? guidedPaths;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
@ -164,7 +170,9 @@ class MapDTO {
other.iconSource == iconSource && other.iconSource == iconSource &&
_deepEquality.equals(other.categories, categories) && _deepEquality.equals(other.categories, categories) &&
other.centerLatitude == centerLatitude && other.centerLatitude == centerLatitude &&
other.centerLongitude == centerLongitude; other.centerLongitude == centerLongitude &&
other.isParcours == isParcours &&
_deepEquality.equals(other.guidedPaths, guidedPaths);
@override @override
int get hashCode => int get hashCode =>
@ -197,11 +205,13 @@ class MapDTO {
(iconSource == null ? 0 : iconSource!.hashCode) + (iconSource == null ? 0 : iconSource!.hashCode) +
(categories == null ? 0 : categories!.hashCode) + (categories == null ? 0 : categories!.hashCode) +
(centerLatitude == null ? 0 : centerLatitude!.hashCode) + (centerLatitude == null ? 0 : centerLatitude!.hashCode) +
(centerLongitude == null ? 0 : centerLongitude!.hashCode); (centerLongitude == null ? 0 : centerLongitude!.hashCode) +
(isParcours == null ? 0 : isParcours!.hashCode) +
(guidedPaths == null ? 0 : guidedPaths!.hashCode);
@override @override
String toString() => String toString() =>
'MapDTO[id=$id, label=$label, title=$title, description=$description, isActive=$isActive, imageId=$imageId, imageSource=$imageSource, configurationId=$configurationId, isSubSection=$isSubSection, parentId=$parentId, type=$type, dateCreation=$dateCreation, order=$order, instanceId=$instanceId, latitude=$latitude, longitude=$longitude, meterZoneGPS=$meterZoneGPS, isBeacon=$isBeacon, beaconId=$beaconId, zoom=$zoom, mapType=$mapType, mapTypeMapbox=$mapTypeMapbox, mapProvider=$mapProvider, points=$points, iconResourceId=$iconResourceId, iconSource=$iconSource, categories=$categories, centerLatitude=$centerLatitude, centerLongitude=$centerLongitude]'; 'MapDTO[id=$id, label=$label, title=$title, description=$description, isActive=$isActive, imageId=$imageId, imageSource=$imageSource, configurationId=$configurationId, isSubSection=$isSubSection, parentId=$parentId, type=$type, dateCreation=$dateCreation, order=$order, instanceId=$instanceId, latitude=$latitude, longitude=$longitude, meterZoneGPS=$meterZoneGPS, isBeacon=$isBeacon, beaconId=$beaconId, zoom=$zoom, mapType=$mapType, mapTypeMapbox=$mapTypeMapbox, mapProvider=$mapProvider, points=$points, iconResourceId=$iconResourceId, iconSource=$iconSource, categories=$categories, centerLatitude=$centerLatitude, centerLongitude=$centerLongitude, isParcours=$isParcours, guidedPaths=$guidedPaths]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -216,12 +226,12 @@ class MapDTO {
json[r'label'] = null; json[r'label'] = null;
} }
if (this.title != null) { if (this.title != null) {
json[r'title'] = this.title; json[r'title'] = this.title!.map((v) => v.toJson()).toList();
} else { } else {
json[r'title'] = null; json[r'title'] = null;
} }
if (this.description != null) { if (this.description != null) {
json[r'description'] = this.description; json[r'description'] = this.description!.map((v) => v.toJson()).toList();
} else { } else {
json[r'description'] = null; json[r'description'] = null;
} }
@ -321,7 +331,7 @@ class MapDTO {
json[r'mapProvider'] = null; json[r'mapProvider'] = null;
} }
if (this.points != null) { if (this.points != null) {
json[r'points'] = this.points; json[r'points'] = this.points!.map((v) => v.toJson()).toList();
} else { } else {
json[r'points'] = null; json[r'points'] = null;
} }
@ -336,7 +346,7 @@ class MapDTO {
json[r'iconSource'] = null; json[r'iconSource'] = null;
} }
if (this.categories != null) { if (this.categories != null) {
json[r'categories'] = this.categories; json[r'categories'] = this.categories!.map((v) => v.toJson()).toList();
} else { } else {
json[r'categories'] = null; json[r'categories'] = null;
} }
@ -350,6 +360,16 @@ class MapDTO {
} else { } else {
json[r'centerLongitude'] = null; json[r'centerLongitude'] = null;
} }
if (this.isParcours != null) {
json[r'isParcours'] = this.isParcours;
} else {
json[r'isParcours'] = null;
}
if (this.guidedPaths != null) {
json[r'guidedPaths'] = this.guidedPaths!.map((v) => v.toJson()).toList();
} else {
json[r'guidedPaths'] = null;
}
return json; return json;
} }
@ -403,6 +423,8 @@ class MapDTO {
categories: CategorieDTO.listFromJson(json[r'categories']), categories: CategorieDTO.listFromJson(json[r'categories']),
centerLatitude: mapValueOfType<String>(json, r'centerLatitude'), centerLatitude: mapValueOfType<String>(json, r'centerLatitude'),
centerLongitude: mapValueOfType<String>(json, r'centerLongitude'), centerLongitude: mapValueOfType<String>(json, r'centerLongitude'),
isParcours: mapValueOfType<bool>(json, r'isParcours'),
guidedPaths: GuidedPathDTO.listFromJson(json[r'guidedPaths']),
); );
} }
return null; return null;

View File

@ -447,7 +447,7 @@ packages:
source: hosted source: hosted
version: "1.5.1" version: "1.5.1"
flutter_map: flutter_map:
dependency: transitive dependency: "direct main"
description: description:
name: flutter_map name: flutter_map
sha256: "87cc8349b8fa5dccda5af50018c7374b6645334a0d680931c1fe11bce88fa5bb" sha256: "87cc8349b8fa5dccda5af50018c7374b6645334a0d680931c1fe11bce88fa5bb"
@ -737,7 +737,7 @@ packages:
source: hosted source: hosted
version: "0.4.13" version: "0.4.13"
latlong2: latlong2:
dependency: transitive dependency: "direct main"
description: description:
name: latlong2 name: latlong2
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe" sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"

View File

@ -49,6 +49,8 @@ dependencies:
multi_select_flutter: ^4.1.3 multi_select_flutter: ^4.1.3
youtube_player_iframe: ^5.0.0 youtube_player_iframe: ^5.0.0
location_picker_flutter_map: ^3.0.1 location_picker_flutter_map: ^3.0.1
flutter_map: ^6.2.1
latlong2: ^0.9.1
go_router: ^16.2.0 go_router: ^16.2.0
#msix: ^2.1.3 #msix: ^2.1.3