Wip changements fantastiquement rapide d'antigravity, très bonne base mais du wip
This commit is contained in:
parent
9e06afb29e
commit
ded4620bf2
144
lib/Components/geometry_input_container.dart
Normal file
144
lib/Components/geometry_input_container.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
361
lib/Components/map_geometry_picker.dart
Normal file
361
lib/Components/map_geometry_picker.dart
Normal 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)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -11,8 +11,8 @@ class RoundedButton extends StatelessWidget {
|
||||
final double? vertical;
|
||||
final double? horizontal;
|
||||
|
||||
const RoundedButton({
|
||||
Key? key,
|
||||
const RoundedButton(
|
||||
{Key? key,
|
||||
required this.text,
|
||||
required this.press,
|
||||
this.icon,
|
||||
@ -20,28 +20,24 @@ class RoundedButton extends StatelessWidget {
|
||||
this.textColor = kWhite,
|
||||
required this.fontSize,
|
||||
this.vertical,
|
||||
this.horizontal
|
||||
}) : super(key: key);
|
||||
this.horizontal})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//Size size = MediaQuery.of(context).size;
|
||||
return TextButton(
|
||||
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))),
|
||||
backgroundColor: MaterialStateColor.resolveWith((states) => color),
|
||||
padding: MaterialStateProperty.all(EdgeInsets.symmetric(
|
||||
vertical: vertical ?? 12,
|
||||
horizontal: horizontal ?? (icon == null ? 20 : 15),
|
||||
)),
|
||||
backgroundColor: MaterialStateProperty.all(color),
|
||||
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
onPressed: () => {
|
||||
press()
|
||||
},
|
||||
child: getValue(icon)
|
||||
);
|
||||
))),
|
||||
onPressed: () => {press()},
|
||||
child: getValue(icon));
|
||||
}
|
||||
|
||||
getValue(icon) {
|
||||
@ -52,15 +48,16 @@ class RoundedButton extends StatelessWidget {
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
text,
|
||||
style: new TextStyle(color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400),
|
||||
style: new TextStyle(
|
||||
color: textColor,
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.w400),
|
||||
maxLines: 2,
|
||||
maxFontSize: fontSize,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 10
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Icon(
|
||||
icon,
|
||||
color: textColor,
|
||||
@ -71,7 +68,8 @@ class RoundedButton extends StatelessWidget {
|
||||
} else {
|
||||
return AutoSizeText(
|
||||
text,
|
||||
style: new TextStyle(color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400),
|
||||
style: new TextStyle(
|
||||
color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400),
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
@ -1,19 +1,17 @@
|
||||
import 'package:flutter/cupertino.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/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 'dart:convert';
|
||||
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'showNewOrUpdateEventAgenda.dart';
|
||||
|
||||
class AgendaConfig extends StatefulWidget {
|
||||
final String? color;
|
||||
final String? label;
|
||||
final AgendaDTO initialValue;
|
||||
final ValueChanged<AgendaDTO> onChanged;
|
||||
|
||||
const AgendaConfig({
|
||||
Key? key,
|
||||
this.color,
|
||||
@ -31,21 +29,20 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
AgendaDTO test = widget.initialValue;
|
||||
agendaDTO = test;
|
||||
super.initState();
|
||||
agendaDTO = widget.initialValue;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Size size = MediaQuery.of(context).size;
|
||||
//Size size = MediaQuery.of(context).size;
|
||||
|
||||
var mapProviderIn = "";
|
||||
switch(agendaDTO.agendaMapProvider) {
|
||||
case MapProvider.Google :
|
||||
switch (agendaDTO.agendaMapProvider) {
|
||||
case MapProvider.Google:
|
||||
mapProviderIn = "Google";
|
||||
break;
|
||||
case MapProvider.MapBox :
|
||||
case MapProvider.MapBox:
|
||||
mapProviderIn = "MapBox";
|
||||
break;
|
||||
default:
|
||||
@ -53,17 +50,16 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
break;
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
height: size.height * 0.3,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
CheckInputContainer(
|
||||
label: "En ligne :",
|
||||
isChecked: agendaDTO.isOnlineAgenda!,
|
||||
isChecked: agendaDTO.isOnlineAgenda ?? true,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
agendaDTO.isOnlineAgenda = value;
|
||||
@ -75,9 +71,10 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
label: "Service carte :",
|
||||
color: Colors.black,
|
||||
initialValue: mapProviderIn,
|
||||
inputValues: map_providers,
|
||||
inputValues: ["Google", "MapBox"],
|
||||
onChanged: (String value) {
|
||||
switch(value) {
|
||||
setState(() {
|
||||
switch (value) {
|
||||
case "Google":
|
||||
agendaDTO.agendaMapProvider = MapProvider.Google;
|
||||
break;
|
||||
@ -86,22 +83,21 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
break;
|
||||
}
|
||||
widget.onChanged(agendaDTO);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
if (agendaDTO.isOnlineAgenda == true)
|
||||
MultiStringInputContainer(
|
||||
label: "Fichiers json :",
|
||||
resourceTypes: [ResourceType.Json, ResourceType.JsonUrl],
|
||||
modalLabel: "JSON",
|
||||
color: kPrimaryColor,
|
||||
initialValue: agendaDTO.resourceIds!,
|
||||
initialValue: agendaDTO.resourceIds ?? [],
|
||||
isTitle: false,
|
||||
onGetResult: (value) {
|
||||
setState(() {
|
||||
if (agendaDTO.resourceIds != value) {
|
||||
agendaDTO.resourceIds = value;
|
||||
//save(true, articleDTO, appContext);
|
||||
widget.onChanged(agendaDTO);
|
||||
}
|
||||
});
|
||||
},
|
||||
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());
|
||||
}
|
||||
);*/
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -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);
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -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/resource_input_container.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';
|
||||
|
||||
class GameConfig extends StatefulWidget {
|
||||
@ -30,33 +29,73 @@ class _GameConfigState extends State<GameConfig> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
GameDTO test = widget.initialValue;
|
||||
/*if(test.puzzleImage == null) {
|
||||
test.puzzleImage = ResourceDTO();
|
||||
}*/
|
||||
gameDTO = test;
|
||||
gameDTO.rows = gameDTO.rows == null ? 3 : gameDTO.rows;
|
||||
gameDTO.cols = gameDTO.cols == null ? 3 : gameDTO.cols;
|
||||
|
||||
gameDTO = widget.initialValue;
|
||||
gameDTO.rows = gameDTO.rows ?? 3;
|
||||
gameDTO.cols = gameDTO.cols ?? 3;
|
||||
gameDTO.gameType = gameDTO.gameType ?? GameTypes.number0;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
return Column(
|
||||
children: [
|
||||
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: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
ResourceInputContainer(
|
||||
label: "Image du puzzle :",
|
||||
initialValue: gameDTO.puzzleImageId == null ? '': gameDTO.puzzleImageId,
|
||||
initialValue: gameDTO.puzzleImageId ?? '',
|
||||
onChanged: (ResourceDTO resourceDTO) {
|
||||
setState(() {
|
||||
if(resourceDTO.id == null)
|
||||
{
|
||||
if (resourceDTO.id == null) {
|
||||
gameDTO.puzzleImageId = null;
|
||||
gameDTO.puzzleImage = null;
|
||||
} else {
|
||||
@ -65,7 +104,7 @@ class _GameConfigState extends State<GameConfig> {
|
||||
}
|
||||
widget.onChanged(gameDTO);
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
Container(
|
||||
height: 100,
|
||||
@ -74,19 +113,23 @@ class _GameConfigState extends State<GameConfig> {
|
||||
modalLabel: "Message départ",
|
||||
fontSize: 20,
|
||||
color: kPrimaryColor,
|
||||
initialValue: gameDTO.messageDebut != null ? gameDTO.messageDebut! : [],
|
||||
resourceTypes: [ResourceType.Image, ResourceType.ImageUrl, ResourceType.VideoUrl, ResourceType.Video, ResourceType.Audio],
|
||||
initialValue: gameDTO.messageDebut ?? [],
|
||||
resourceTypes: [
|
||||
ResourceType.Image,
|
||||
ResourceType.ImageUrl,
|
||||
ResourceType.VideoUrl,
|
||||
ResourceType.Video,
|
||||
ResourceType.Audio
|
||||
],
|
||||
onGetResult: (value) {
|
||||
if (gameDTO.messageDebut != value) {
|
||||
setState(() {
|
||||
gameDTO.messageDebut = value;
|
||||
widget.onChanged(gameDTO);
|
||||
});
|
||||
}
|
||||
},
|
||||
maxLines: 1,
|
||||
isTitle: false
|
||||
)
|
||||
isTitle: false,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 100,
|
||||
@ -95,19 +138,23 @@ class _GameConfigState extends State<GameConfig> {
|
||||
modalLabel: "Message fin",
|
||||
fontSize: 20,
|
||||
color: kPrimaryColor,
|
||||
initialValue: gameDTO.messageFin != null ? gameDTO.messageFin! : [],
|
||||
resourceTypes: [ResourceType.Image, ResourceType.ImageUrl, ResourceType.VideoUrl, ResourceType.Video, ResourceType.Audio],
|
||||
initialValue: gameDTO.messageFin ?? [],
|
||||
resourceTypes: [
|
||||
ResourceType.Image,
|
||||
ResourceType.ImageUrl,
|
||||
ResourceType.VideoUrl,
|
||||
ResourceType.Video,
|
||||
ResourceType.Audio
|
||||
],
|
||||
onGetResult: (value) {
|
||||
if (gameDTO.messageFin != value) {
|
||||
setState(() {
|
||||
gameDTO.messageFin = value;
|
||||
widget.onChanged(gameDTO);
|
||||
});
|
||||
}
|
||||
},
|
||||
maxLines: 1,
|
||||
isTitle: false
|
||||
)
|
||||
isTitle: false,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -118,7 +165,7 @@ class _GameConfigState extends State<GameConfig> {
|
||||
height: 100,
|
||||
child: NumberInputContainer(
|
||||
label: "Nombre de lignes :",
|
||||
initialValue: gameDTO.rows!,
|
||||
initialValue: gameDTO.rows ?? 3,
|
||||
isSmall: true,
|
||||
maxLength: 2,
|
||||
onChanged: (value) {
|
||||
@ -128,7 +175,8 @@ class _GameConfigState extends State<GameConfig> {
|
||||
widget.onChanged(gameDTO);
|
||||
});
|
||||
} 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,
|
||||
child: NumberInputContainer(
|
||||
label: "Nombre de colonnes :",
|
||||
initialValue: gameDTO.cols!,
|
||||
initialValue: gameDTO.cols ?? 3,
|
||||
isSmall: true,
|
||||
maxLength: 2,
|
||||
onChanged: (value) {
|
||||
@ -147,14 +195,18 @@ class _GameConfigState extends State<GameConfig> {
|
||||
widget.onChanged(gameDTO);
|
||||
});
|
||||
} catch (e) {
|
||||
showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null);
|
||||
showNotification(Colors.orange, kWhite,
|
||||
'Cela doit être un chiffre', context, null);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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/common_loader.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/Screens/Configurations/Section/SubSection/Map/category_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/string_input_container.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/client.dart';
|
||||
import 'package:manager_app/constants.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';
|
||||
|
||||
@ -47,12 +45,13 @@ class _MapConfigState extends State<MapConfig> {
|
||||
late MapDTO mapDTO;
|
||||
late List<GeoPointDTO> pointsToShow = [];
|
||||
//List<String>? selectedCategories = [];
|
||||
String mapType= "hybrid";
|
||||
String mapTypeMapBox= "standard";
|
||||
String mapType = "hybrid";
|
||||
String mapTypeMapBox = "standard";
|
||||
|
||||
String filterSearch = '';
|
||||
|
||||
final ValueNotifier<List<int>?> selectedCategoriesNotifier = ValueNotifier([]);
|
||||
final ValueNotifier<List<int>?> selectedCategoriesNotifier =
|
||||
ValueNotifier([]);
|
||||
final ValueNotifier<String?> searchNotifier = ValueNotifier("");
|
||||
|
||||
@override
|
||||
@ -61,8 +60,8 @@ class _MapConfigState extends State<MapConfig> {
|
||||
//mapDTO = MapDTO.fromJson(json.decode(widget.initialValue))!;
|
||||
mapDTO = widget.initialValue;
|
||||
|
||||
if(mapDTO.mapType != null) {
|
||||
switch(mapDTO.mapType!.value) {
|
||||
if (mapDTO.mapType != null) {
|
||||
switch (mapDTO.mapType!.value) {
|
||||
case 0:
|
||||
mapType = "none";
|
||||
break;
|
||||
@ -81,8 +80,8 @@ class _MapConfigState extends State<MapConfig> {
|
||||
}
|
||||
}
|
||||
|
||||
if(mapDTO.mapTypeMapbox != null) {
|
||||
switch(mapDTO.mapTypeMapbox!.value) {
|
||||
if (mapDTO.mapTypeMapbox != null) {
|
||||
switch (mapDTO.mapTypeMapbox!.value) {
|
||||
case 0:
|
||||
mapTypeMapBox = "standard";
|
||||
break;
|
||||
@ -114,11 +113,11 @@ class _MapConfigState extends State<MapConfig> {
|
||||
Size size = MediaQuery.of(context).size;
|
||||
|
||||
var mapProviderIn = "";
|
||||
switch(mapDTO.mapProvider) {
|
||||
case MapProvider.Google :
|
||||
switch (mapDTO.mapProvider) {
|
||||
case MapProvider.Google:
|
||||
mapProviderIn = "Google";
|
||||
break;
|
||||
case MapProvider.MapBox :
|
||||
case MapProvider.MapBox:
|
||||
mapProviderIn = "MapBox";
|
||||
break;
|
||||
default:
|
||||
@ -126,7 +125,25 @@ class _MapConfigState extends State<MapConfig> {
|
||||
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(
|
||||
child: Column(
|
||||
children: [
|
||||
@ -137,7 +154,8 @@ class _MapConfigState extends State<MapConfig> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
SingleSelectContainer(
|
||||
label: "Service :",
|
||||
@ -145,89 +163,92 @@ class _MapConfigState extends State<MapConfig> {
|
||||
initialValue: mapProviderIn,
|
||||
inputValues: map_providers,
|
||||
onChanged: (String value) {
|
||||
switch(value) {
|
||||
switch (value) {
|
||||
case "Google":
|
||||
mapDTO.mapProvider = MapProvider.Google;
|
||||
mapDTO.mapProvider =
|
||||
MapProvider.Google;
|
||||
break;
|
||||
case "MapBox":
|
||||
mapDTO.mapProvider = MapProvider.MapBox;
|
||||
mapDTO.mapProvider =
|
||||
MapProvider.MapBox;
|
||||
break;
|
||||
}
|
||||
//widget.onChanged(jsonEncode(mapDTO).toString());
|
||||
widget.onChanged(mapDTO);
|
||||
}
|
||||
),
|
||||
}),
|
||||
GeolocInputContainer(
|
||||
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,
|
||||
onChanged: (LatLong? localisation) {
|
||||
if(localisation != null) {
|
||||
mapDTO.centerLongitude = localisation.longitude.toString();
|
||||
mapDTO.centerLatitude = localisation.latitude.toString();
|
||||
if (localisation != null) {
|
||||
mapDTO.centerLongitude =
|
||||
localisation.longitude.toString();
|
||||
mapDTO.centerLatitude =
|
||||
localisation.latitude.toString();
|
||||
}
|
||||
//widget.onChanged(jsonEncode(mapDTO).toString());
|
||||
widget.onChanged(mapDTO);
|
||||
},
|
||||
isSmall: true
|
||||
),
|
||||
// Icon
|
||||
isSmall: true),
|
||||
ResourceInputContainer(
|
||||
label: "Icône:",
|
||||
initialValue: mapDTO.iconResourceId,
|
||||
color: kPrimaryColor,
|
||||
imageFit: BoxFit.contain,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
if(resource.id == null) {
|
||||
if (resource.id == null) {
|
||||
mapDTO.iconSource = null;
|
||||
mapDTO.iconResourceId = null;
|
||||
} else {
|
||||
mapDTO.iconResourceId = resource.id;
|
||||
mapDTO.iconSource = resource.url;
|
||||
}
|
||||
//widget.onChanged(jsonEncode(mapDTO).toString());
|
||||
widget.onChanged(mapDTO);
|
||||
},
|
||||
isSmall: true
|
||||
),
|
||||
]
|
||||
),
|
||||
isSmall: true),
|
||||
]),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
if(mapDTO.mapProvider == MapProvider.Google)
|
||||
// Map Type
|
||||
if (mapDTO.mapProvider == MapProvider.Google)
|
||||
DropDownInputContainer(
|
||||
label: "Type:",
|
||||
values: map_types,
|
||||
initialValue: mapType,
|
||||
onChange: (String? value) {
|
||||
mapDTO.mapType = MapTypeApp.fromJson(value);
|
||||
//widget.onChanged(jsonEncode(mapDTO).toString());
|
||||
mapDTO.mapType =
|
||||
MapTypeApp.fromJson(value);
|
||||
widget.onChanged(mapDTO);
|
||||
},
|
||||
),
|
||||
if(mapDTO.mapProvider == MapProvider.MapBox)
|
||||
if (mapDTO.mapProvider == MapProvider.MapBox)
|
||||
DropDownInputContainer(
|
||||
label: "Type:",
|
||||
values: map_types_mapBox,
|
||||
initialValue: mapTypeMapBox,
|
||||
onChange: (String? value) {
|
||||
mapDTO.mapTypeMapbox = MapTypeMapBox.fromJson(value);
|
||||
//widget.onChanged(jsonEncode(mapDTO).toString());
|
||||
mapDTO.mapTypeMapbox =
|
||||
MapTypeMapBox.fromJson(value);
|
||||
widget.onChanged(mapDTO);
|
||||
},
|
||||
),
|
||||
// Zoom
|
||||
SliderInputContainer(
|
||||
label: "Zoom:",
|
||||
initialValue: mapDTO.zoom != null ? mapDTO.zoom!.toDouble() : 18,
|
||||
initialValue: mapDTO.zoom != null
|
||||
? mapDTO.zoom!.toDouble()
|
||||
: 18,
|
||||
color: kPrimaryColor,
|
||||
min: 0,
|
||||
max: 30,
|
||||
onChanged: (double value) {
|
||||
mapDTO.zoom = value.toInt();
|
||||
//widget.onChanged(jsonEncode(mapDTO).toString());
|
||||
widget.onChanged(mapDTO);
|
||||
},
|
||||
),
|
||||
@ -235,18 +256,21 @@ class _MapConfigState extends State<MapConfig> {
|
||||
height: 70,
|
||||
child: CategoryInputContainer(
|
||||
label: "Catégories :",
|
||||
initialValue: mapDTO.categories != null ? mapDTO.categories! : [],
|
||||
initialValue: mapDTO.categories != null
|
||||
? mapDTO.categories!
|
||||
: [],
|
||||
color: kPrimaryColor,
|
||||
onChanged: (List<CategorieDTO>? value) {
|
||||
if(value != null) {
|
||||
if (value != null) {
|
||||
mapDTO.categories = value;
|
||||
|
||||
//update point's category
|
||||
if(mapDTO.points != null) {
|
||||
if (mapDTO.points != null) {
|
||||
mapDTO.points!.forEach((p) {
|
||||
// Check if category still exist - Delete
|
||||
if(p.categorieId != null && !mapDTO.categories!.map((c) => c.id).any((e) => e != null && e == p.categorieId))
|
||||
{
|
||||
if (p.categorieId != null &&
|
||||
!mapDTO.categories!
|
||||
.map((c) => c.id)
|
||||
.any((e) =>
|
||||
e != null &&
|
||||
e == p.categorieId)) {
|
||||
p.categorieId = null;
|
||||
}
|
||||
});
|
||||
@ -262,63 +286,143 @@ class _MapConfigState extends State<MapConfig> {
|
||||
),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: getGeoPoints((appContext.getContext() as ManagerAppContext).clientAPI!),
|
||||
future: getGeoPoints(
|
||||
(appContext.getContext() as ManagerAppContext)
|
||||
.clientAPI!),
|
||||
builder: (context, AsyncSnapshot<dynamic> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.waiting) {
|
||||
return Center(child: CommonLoader());
|
||||
} else {
|
||||
if(snapshot.connectionState == ConnectionState.done)
|
||||
{
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done) {
|
||||
mapDTO.points = snapshot.data;
|
||||
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()));
|
||||
selectedCategoriesNotifier.value = mapDTO.categories!.map((categorie) => categorie.id!).toList();
|
||||
pointsToShow.sort((a, b) => a.title!
|
||||
.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(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
border: Border.all(width: 1.5, color: kSecond)
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
border: Border.all(
|
||||
width: 1.5, color: kSecond)),
|
||||
child: Stack(children: [
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 100),
|
||||
constraints:
|
||||
BoxConstraints(minHeight: 100),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 95, left: 10, right: 10, bottom: 10),
|
||||
child: ValueListenableBuilder<String?>(
|
||||
valueListenable: 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();
|
||||
padding: const EdgeInsets.only(
|
||||
top: 95,
|
||||
left: 10,
|
||||
right: 10,
|
||||
bottom: 10),
|
||||
child:
|
||||
ValueListenableBuilder<String?>(
|
||||
valueListenable:
|
||||
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 {
|
||||
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(
|
||||
shrinkWrap: true,
|
||||
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 8),
|
||||
itemCount: pointsToShow.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),
|
||||
return GridView
|
||||
.builder(
|
||||
shrinkWrap:
|
||||
true,
|
||||
gridDelegate:
|
||||
new SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount:
|
||||
8),
|
||||
itemCount:
|
||||
pointsToShow
|
||||
.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(
|
||||
@ -336,22 +440,52 @@ class _MapConfigState extends State<MapConfig> {
|
||||
label: null,
|
||||
color: kSecond,
|
||||
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,
|
||||
isHTMLLabel : true,
|
||||
values: mapDTO.categories!.map((categorie) => categorie.label!.firstWhere((element) => element.language == 'FR').value!).toList(),
|
||||
isHTMLLabel: true,
|
||||
values: mapDTO.categories!
|
||||
.map((categorie) => categorie
|
||||
.label!
|
||||
.firstWhere((element) =>
|
||||
element.language ==
|
||||
'FR')
|
||||
.value!)
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
var tempOutput = 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();
|
||||
var tempOutput =
|
||||
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(
|
||||
top: 0,
|
||||
right: 150,
|
||||
child: Container(
|
||||
height: size.height*0.1,
|
||||
constraints: BoxConstraints(minHeight: 85),
|
||||
height: size.height * 0.1,
|
||||
constraints:
|
||||
BoxConstraints(minHeight: 85),
|
||||
child: StringInputContainer(
|
||||
label: "Recherche:",
|
||||
isSmall: true,
|
||||
@ -369,42 +503,44 @@ class _MapConfigState extends State<MapConfig> {
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showNewOrUpdateGeoPoint(
|
||||
mapDTO,
|
||||
null,
|
||||
mapDTO, null,
|
||||
(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 {
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionMapApi!.sectionMapCreate(mapDTO.id!, geoPoint);
|
||||
showNotification(kSuccess, kWhite, 'Le point géographique a été créé avec succès', context, null);
|
||||
await (appContext.getContext()
|
||||
as ManagerAppContext)
|
||||
.clientAPI!
|
||||
.sectionMapApi!
|
||||
.sectionMapCreate(
|
||||
mapDTO.id!, geoPoint);
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Le point géographique a été créé avec succès',
|
||||
context,
|
||||
null);
|
||||
setState(() {
|
||||
// refresh ui
|
||||
print("Refresh UI");
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors de la création du point géographique', context, null);
|
||||
} catch (e) {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Une erreur est survenue lors de la création du point géographique',
|
||||
context,
|
||||
null);
|
||||
}
|
||||
},
|
||||
appContext,
|
||||
context);
|
||||
}, appContext, context);
|
||||
},
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.width * 0.04,
|
||||
width: MediaQuery.of(context).size.width * 0.04,
|
||||
height: MediaQuery.of(context)
|
||||
.size
|
||||
.width *
|
||||
0.04,
|
||||
width: MediaQuery.of(context)
|
||||
.size
|
||||
.width *
|
||||
0.04,
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
color: kTextLightColor,
|
||||
@ -413,13 +549,15 @@ class _MapConfigState extends State<MapConfig> {
|
||||
decoration: BoxDecoration(
|
||||
color: kSuccess,
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
borderRadius:
|
||||
BorderRadius.circular(20.0),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: kSecond,
|
||||
spreadRadius: 0.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 {
|
||||
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 {
|
||||
List<GeoPointDTO>? geoPoints = await client.sectionMapApi!.sectionMapGetAllGeoPointsFromSection(widget.initialValue.id!);
|
||||
List<GeoPointDTO>? geoPoints = await client.sectionMapApi!
|
||||
.sectionMapGetAllGeoPointsFromSection(widget.initialValue.id!);
|
||||
return geoPoints ?? [];
|
||||
}
|
||||
|
||||
@ -455,13 +614,15 @@ class _MapConfigState extends State<MapConfig> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: HtmlWidget(
|
||||
point != null ? point.title == null ? "" : point.title![0].value! : "",
|
||||
point != null
|
||||
? point.title == null
|
||||
? ""
|
||||
: point.title![0].value!
|
||||
: "",
|
||||
//textAlign: TextAlign.left,
|
||||
customStylesBuilder: (element) {
|
||||
return {'text-align': 'center'};
|
||||
},
|
||||
textStyle: TextStyle(fontSize: 20)
|
||||
),
|
||||
}, textStyle: TextStyle(fontSize: 20)),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
@ -478,19 +639,30 @@ class _MapConfigState extends State<MapConfig> {
|
||||
left: 0,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showNewOrUpdateGeoPoint(
|
||||
mapDTO,
|
||||
pointsToShow[index],
|
||||
showNewOrUpdateGeoPoint(mapDTO, pointsToShow[index],
|
||||
(GeoPointDTO geoPoint) async {
|
||||
try {
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionMapApi!.sectionMapUpdate(geoPoint);
|
||||
showNotification(kSuccess, kWhite, 'Le point géographique a été mis à jour avec succès', context, null);
|
||||
await (appContext.getContext() as ManagerAppContext)
|
||||
.clientAPI!
|
||||
.sectionMapApi!
|
||||
.sectionMapUpdate(geoPoint);
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Le point géographique a été mis à jour avec succès',
|
||||
context,
|
||||
null);
|
||||
setState(() {
|
||||
// refresh ui
|
||||
print("Refresh UI");
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors de la mise à jour du point géographique', context, null);
|
||||
} catch (e) {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Une erreur est survenue lors de la mise à jour du point géographique',
|
||||
context,
|
||||
null);
|
||||
}
|
||||
|
||||
/*setState(() {
|
||||
@ -503,16 +675,13 @@ class _MapConfigState extends State<MapConfig> {
|
||||
//widget.onChanged(jsonEncode(mapDTO).toString());
|
||||
widget.onChanged(mapDTO);
|
||||
});*/
|
||||
},
|
||||
appContext,
|
||||
context);
|
||||
}, appContext, context);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.edit,
|
||||
color: kPrimaryColor,
|
||||
size: 20.0,
|
||||
)
|
||||
),
|
||||
)),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
@ -521,30 +690,38 @@ class _MapConfigState extends State<MapConfig> {
|
||||
onTap: () async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir supprimer ce point géographique ?",
|
||||
() {},
|
||||
() async {
|
||||
|
||||
() {}, () async {
|
||||
try {
|
||||
var pointToRemove = pointsToShow[index];
|
||||
(appContext.getContext() as ManagerAppContext).clientAPI!.sectionMapApi!.sectionMapDelete(pointToRemove.id!);
|
||||
showNotification(kSuccess, kWhite, 'Le point géographique a été supprimé avec succès', context, null);
|
||||
(appContext.getContext() as ManagerAppContext)
|
||||
.clientAPI!
|
||||
.sectionMapApi!
|
||||
.sectionMapDelete(pointToRemove.id!);
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Le point géographique a été supprimé avec succès',
|
||||
context,
|
||||
null);
|
||||
// refresh UI
|
||||
setState(() {
|
||||
print("Refresh UI");
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors de la suppression du point géographique', context, null);
|
||||
} catch (e) {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Une erreur est survenue lors de la suppression du point géographique',
|
||||
context,
|
||||
null);
|
||||
}
|
||||
},
|
||||
context
|
||||
);
|
||||
}, context);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.delete,
|
||||
color: kError,
|
||||
size: 20.0,
|
||||
)
|
||||
),
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -552,7 +729,6 @@ class _MapConfigState extends State<MapConfig> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boxDecoration(GeoPointDTO geoPointDTO, appContext) {
|
||||
return BoxDecoration(
|
||||
color: kBackgroundColor,
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
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/common_loader.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/rounded_button.dart';
|
||||
import 'package:manager_app/Components/string_input_container.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/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Function getResult, AppContext appContext, BuildContext context) {
|
||||
GeoPointDTO geoPointDTO = new GeoPointDTO();
|
||||
void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO,
|
||||
Function getResult, AppContext appContext, BuildContext context) {
|
||||
GeoPointDTO geoPointDTO = GeoPointDTO();
|
||||
|
||||
if (inputGeoPointDTO != null) {
|
||||
geoPointDTO = inputGeoPointDTO;
|
||||
@ -21,265 +20,225 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
|
||||
geoPointDTO.title = <TranslationDTO>[];
|
||||
geoPointDTO.description = <TranslationDTO>[];
|
||||
geoPointDTO.contents = <ContentDTO>[];
|
||||
geoPointDTO.schedules = <TranslationDTO>[];
|
||||
geoPointDTO.prices = <TranslationDTO>[];
|
||||
geoPointDTO.phone = <TranslationDTO>[];
|
||||
geoPointDTO.email = <TranslationDTO>[];
|
||||
geoPointDTO.site = <TranslationDTO>[];
|
||||
|
||||
ManagerAppContext managerAppContext = appContext.getContext();
|
||||
managerAppContext.selectedConfiguration!.languages!.forEach((element) {
|
||||
var translationDTO = new TranslationDTO();
|
||||
var translationDTO = TranslationDTO();
|
||||
translationDTO.language = element;
|
||||
translationDTO.value = "";
|
||||
geoPointDTO.title!.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(
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20.0))
|
||||
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.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(
|
||||
width: size.width *0.85,
|
||||
SizedBox(height: 16),
|
||||
// Corps scrollable
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Point géographique", style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
width: size.width *0.75,
|
||||
height: 350,
|
||||
child: FlutterLocationPicker(
|
||||
initZoom: 14,
|
||||
initPosition: initPosition,
|
||||
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]);
|
||||
// Géométrie
|
||||
GeometryInputContainer(
|
||||
label: "Géométrie (Point/Ligne/Zone) :",
|
||||
initialGeometry: geoPointDTO.geometry,
|
||||
initialColor: geoPointDTO.polyColor,
|
||||
onSave: (geometry, color) {
|
||||
setState(() {
|
||||
geoPointDTO.geometry = geometry;
|
||||
geoPointDTO.polyColor = color;
|
||||
});
|
||||
},
|
||||
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,
|
||||
),
|
||||
),
|
||||
/*Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
SizedBox(height: 12),
|
||||
// Ligne 1 : Titre | Description | Site
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 100,
|
||||
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),
|
||||
width: thirdWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Titre affiché:",
|
||||
label: "Titre :",
|
||||
modalLabel: "Titre",
|
||||
fontSize: 20,
|
||||
fontSize: 16,
|
||||
isHTML: true,
|
||||
color: kPrimaryColor,
|
||||
initialValue: geoPointDTO.title != null ? geoPointDTO.title! : [],
|
||||
initialValue: geoPointDTO.title ?? [],
|
||||
onGetResult: (value) {
|
||||
if (geoPointDTO.title != value) {
|
||||
geoPointDTO.title = value;
|
||||
}
|
||||
},
|
||||
maxLines: 1,
|
||||
isTitle: true
|
||||
isTitle: true,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
SizedBox(width: 20),
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Description affichée:",
|
||||
label: "Description :",
|
||||
modalLabel: "Description",
|
||||
fontSize: 20,
|
||||
fontSize: 16,
|
||||
isHTML: true,
|
||||
color: kPrimaryColor,
|
||||
initialValue: geoPointDTO.description != null ? geoPointDTO.description! : [],
|
||||
initialValue: geoPointDTO.description ?? [],
|
||||
isMandatory: false,
|
||||
onGetResult: (value) {
|
||||
if (geoPointDTO.description != value) {
|
||||
geoPointDTO.description = value;
|
||||
}
|
||||
},
|
||||
maxLines: 1,
|
||||
isTitle: false
|
||||
isTitle: false,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
SizedBox(width: 20),
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Site:",
|
||||
label: "Site :",
|
||||
modalLabel: "Site web",
|
||||
fontSize: 20,
|
||||
fontSize: 16,
|
||||
isHTML: true,
|
||||
color: kPrimaryColor,
|
||||
initialValue: geoPointDTO.site != null ? geoPointDTO.site! : [],
|
||||
initialValue: geoPointDTO.site ?? [],
|
||||
isMandatory: false,
|
||||
onGetResult: (value) {
|
||||
if (geoPointDTO.site != value) {
|
||||
geoPointDTO.site = value;
|
||||
}
|
||||
},
|
||||
maxLines: 1,
|
||||
isTitle: true
|
||||
isTitle: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 100,
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
SizedBox(height: 12),
|
||||
// Ligne 2 : Prix | Tel | Email
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Prix:",
|
||||
label: "Prix :",
|
||||
modalLabel: "Prix",
|
||||
fontSize: 20,
|
||||
fontSize: 16,
|
||||
isHTML: true,
|
||||
color: kPrimaryColor,
|
||||
initialValue: geoPointDTO.prices != null ? geoPointDTO.prices! : [],
|
||||
initialValue: geoPointDTO.prices ?? [],
|
||||
isMandatory: false,
|
||||
onGetResult: (value) {
|
||||
if (geoPointDTO.prices != value) {
|
||||
geoPointDTO.prices = value;
|
||||
}
|
||||
},
|
||||
maxLines: 1,
|
||||
isTitle: false
|
||||
isTitle: false,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
SizedBox(width: 20),
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Téléphone:",
|
||||
label: "Tel :",
|
||||
modalLabel: "Téléphone",
|
||||
fontSize: 20,
|
||||
fontSize: 16,
|
||||
isHTML: true,
|
||||
color: kPrimaryColor,
|
||||
initialValue: geoPointDTO.phone != null ? geoPointDTO.phone! : [],
|
||||
initialValue: geoPointDTO.phone ?? [],
|
||||
isMandatory: false,
|
||||
onGetResult: (value) {
|
||||
if (geoPointDTO.phone != value) {
|
||||
geoPointDTO.phone = value;
|
||||
}
|
||||
},
|
||||
maxLines: 1,
|
||||
isTitle: true
|
||||
isTitle: true,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
SizedBox(width: 20),
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Email:",
|
||||
label: "Email :",
|
||||
modalLabel: "Email",
|
||||
fontSize: 20,
|
||||
fontSize: 16,
|
||||
isHTML: true,
|
||||
color: kPrimaryColor,
|
||||
initialValue: geoPointDTO.email != null ? geoPointDTO.email! : [],
|
||||
initialValue: geoPointDTO.email ?? [],
|
||||
isMandatory: false,
|
||||
onGetResult: (value) {
|
||||
if (geoPointDTO.email != value) {
|
||||
geoPointDTO.email = value;
|
||||
}
|
||||
},
|
||||
maxLines: 1,
|
||||
isTitle: true
|
||||
isTitle: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 100,
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
SizedBox(height: 12),
|
||||
// Ligne 3 : Horaires | Image | Catégorie
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ResourceInputContainer(
|
||||
label: "Image principal:",
|
||||
initialValue: geoPointDTO.imageResourceId != null ? geoPointDTO.imageResourceId : null,
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
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,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
if(resource.id == null) {
|
||||
if (resource.id == null) {
|
||||
geoPointDTO.imageResourceId = null;
|
||||
geoPointDTO.imageUrl = null;
|
||||
} else {
|
||||
@ -288,29 +247,33 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
|
||||
}
|
||||
},
|
||||
),
|
||||
if(mapDTO.categories != null && mapDTO.categories!.isNotEmpty)
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
if (mapDTO.categories != null &&
|
||||
mapDTO.categories!.isNotEmpty)
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: DropDownInputContainerCategories(
|
||||
label: "Choisir une catégorie:",
|
||||
label: "Catégorie :",
|
||||
categories: mapDTO.categories!,
|
||||
initialValue: geoPointDTO.categorieId,
|
||||
onChange: (CategorieDTO? value) {
|
||||
if(value != null && value.order != -1)
|
||||
{
|
||||
if (value != null && value.order != -1) {
|
||||
geoPointDTO.categorieId = value.id;
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
geoPointDTO.categorieId = null;
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
SizedBox(width: thirdWidth),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
// Liste de contenus
|
||||
Container(
|
||||
height: size.height * 0.33,
|
||||
height: screenHeight * 0.28,
|
||||
decoration: BoxDecoration(
|
||||
color: kWhite,
|
||||
shape: BoxShape.rectangle,
|
||||
@ -321,7 +284,7 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
|
||||
color: kSecond,
|
||||
spreadRadius: 0.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
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
SizedBox(height: 16),
|
||||
// Boutons
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Align(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: Container(
|
||||
width: 175,
|
||||
height: 70,
|
||||
SizedBox(
|
||||
height: 46,
|
||||
child: RoundedButton(
|
||||
text: "Annuler",
|
||||
icon: Icons.undo,
|
||||
@ -357,34 +316,38 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO, Funct
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
fontSize: 20,
|
||||
fontSize: 15,
|
||||
horizontal: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: Container(
|
||||
width: geoPointDTO != null ? 220: 150,
|
||||
height: 70,
|
||||
SizedBox(width: 12),
|
||||
SizedBox(
|
||||
height: 46,
|
||||
child: RoundedButton(
|
||||
text: geoPointDTO != null ? "Sauvegarder" : "Créer",
|
||||
text:
|
||||
geoPointDTO.id != null ? "Sauvegarder" : "Créer",
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
press: () {
|
||||
//print("TODO");
|
||||
if (geoPointDTO.geometry != null && geoPointDTO.geometry!.coordinates != null) {
|
||||
if (geoPointDTO.geometry != null &&
|
||||
geoPointDTO.geometry!.coordinates != null) {
|
||||
getResult(geoPointDTO);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
fontSize: 20,
|
||||
),
|
||||
fontSize: 15,
|
||||
horizontal: 24,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
), context: context
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -39,6 +39,7 @@ import 'package:pasteboard/pasteboard.dart';
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'SubSection/Weather/weather_config.dart';
|
||||
import 'SubSection/Event/event_config.dart';
|
||||
|
||||
class SectionDetailScreen extends StatefulWidget {
|
||||
final String id;
|
||||
@ -61,18 +62,20 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
Object? rawSectionData;
|
||||
|
||||
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) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
rawSectionData = snapshot.data;
|
||||
|
||||
var nullableSection = SectionDTO.fromJson(rawSectionData);
|
||||
|
||||
if(nullableSection != null) {
|
||||
if (nullableSection != null) {
|
||||
sectionDTO = nullableSection;
|
||||
return Stack(
|
||||
children: [
|
||||
bodySection(rawSectionData, size, appContext, context, globalKey),
|
||||
bodySection(
|
||||
rawSectionData, size, appContext, context, globalKey),
|
||||
Align(
|
||||
alignment: AlignmentDirectional.bottomCenter,
|
||||
child: Container(
|
||||
@ -83,23 +86,22 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
],
|
||||
);
|
||||
} 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) {
|
||||
return Text("No data");
|
||||
} else {
|
||||
return Center(
|
||||
child: Container(
|
||||
height: size.height * 0.2,
|
||||
child: CommonLoader()
|
||||
)
|
||||
);
|
||||
height: size.height * 0.2, 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();
|
||||
//SectionDTO? sectionDTO = SectionDTO.fromJson(rawSectionDTO);
|
||||
|
||||
@ -131,26 +133,33 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
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!)
|
||||
DownloadPDF(sections: [sectionDTO]),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
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: const EdgeInsets.all(5.0),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
ManagerAppContext managerAppContext = appContext.getContext();
|
||||
ManagerAppContext managerAppContext =
|
||||
appContext.getContext();
|
||||
managerAppContext.selectedSection = null;
|
||||
appContext.setContext(managerAppContext);
|
||||
},
|
||||
@ -159,9 +168,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
Icons.arrow_back,
|
||||
color: kPrimaryColor,
|
||||
size: 50.0,
|
||||
)
|
||||
)
|
||||
),
|
||||
))),
|
||||
),
|
||||
)
|
||||
],
|
||||
@ -190,25 +197,37 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
var image = await _captureAndSharePng(globalKey, sectionDTO.id!);
|
||||
var image = await _captureAndSharePng(
|
||||
globalKey, sectionDTO.id!);
|
||||
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(
|
||||
width: size.width *0.1,
|
||||
width: size.width * 0.1,
|
||||
height: 125,
|
||||
child: RepaintBoundary(
|
||||
key: globalKey,
|
||||
child: QrImageView(
|
||||
padding: EdgeInsets.only(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!}",
|
||||
padding: EdgeInsets.only(
|
||||
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,
|
||||
size: 50.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SelectableText(sectionDTO.id!, style: new TextStyle(fontSize: 15))
|
||||
SelectableText(sectionDTO.id!,
|
||||
style: new TextStyle(fontSize: 15))
|
||||
],
|
||||
),
|
||||
CheckInputContainer(
|
||||
@ -221,17 +240,24 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
});
|
||||
},
|
||||
),
|
||||
if(sectionDTO.isBeacon!)
|
||||
if (sectionDTO.isBeacon!)
|
||||
NumberInputContainer(
|
||||
label: "Identifiant Beacon :",
|
||||
initialValue: sectionDTO.beaconId != null ? sectionDTO.beaconId! : 0,
|
||||
initialValue: sectionDTO.beaconId != null
|
||||
? sectionDTO.beaconId!
|
||||
: 0,
|
||||
isSmall: true,
|
||||
onChanged: (value) {
|
||||
try {
|
||||
sectionDTO.beaconId = int.parse(value);
|
||||
} catch (e) {
|
||||
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);
|
||||
}
|
||||
},
|
||||
),
|
||||
@ -297,7 +323,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
initialValue: sectionDTO.imageId,
|
||||
color: kPrimaryColor,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
if(resource.id == null) {
|
||||
if (resource.id == null) {
|
||||
sectionDTO.imageId = null;
|
||||
sectionDTO.imageSource = null;
|
||||
} else {
|
||||
@ -315,7 +341,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
),// FIELDS SECTION
|
||||
), // FIELDS SECTION
|
||||
Container(
|
||||
//width: size.width * 0.8,
|
||||
height: size.height * 0.5,
|
||||
@ -326,8 +352,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
decoration: BoxDecoration(
|
||||
//color: Colors.lightGreen,
|
||||
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 {
|
||||
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);
|
||||
if(nullableSection != null)
|
||||
{
|
||||
if (nullableSection != null) {
|
||||
managerAppContext.selectedSection = nullableSection!;
|
||||
appContext.setContext(managerAppContext);
|
||||
}
|
||||
@ -397,22 +424,22 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
|
||||
Future<void> delete(AppContext appContext) async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir supprimer cette section ?",
|
||||
() {},
|
||||
() async {
|
||||
"Êtes-vous sûr de vouloir supprimer cette section ?", () {}, () async {
|
||||
ManagerAppContext managerAppContext = appContext.getContext();
|
||||
await managerAppContext.clientAPI!.sectionApi!.sectionDelete(sectionDTO.id!);
|
||||
await managerAppContext.clientAPI!.sectionApi!
|
||||
.sectionDelete(sectionDTO.id!);
|
||||
managerAppContext.selectedSection = null;
|
||||
appContext.setContext(managerAppContext);
|
||||
},
|
||||
context
|
||||
);
|
||||
}, context);
|
||||
}
|
||||
|
||||
Future<void> save(bool isTraduction, AppContext appContext) async {
|
||||
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);
|
||||
|
||||
ManagerAppContext managerAppContext = appContext.getContext();
|
||||
@ -420,14 +447,20 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
appContext.setContext(managerAppContext);
|
||||
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
getSpecificData(Object? rawSectionData, AppContext appContext) {
|
||||
switch(sectionDTO.type) {
|
||||
switch (sectionDTO.type) {
|
||||
case SectionType.Map:
|
||||
MapDTO mapDTO = MapDTO.fromJson(rawSectionData)!;
|
||||
sectionDetailDTO = mapDTO;
|
||||
@ -529,19 +562,28 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
sectionDetailDTO = updatedWeather;
|
||||
},
|
||||
);
|
||||
case SectionType.Event:
|
||||
SectionEventDTO eventDTO = SectionEventDTO.fromJson(rawSectionData)!;
|
||||
sectionDetailDTO = eventDTO;
|
||||
return EventConfig(
|
||||
initialValue: eventDTO,
|
||||
onChanged: (SectionEventDTO updatedEvent) {
|
||||
sectionDetailDTO = updatedEvent;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
updateSectionDetail() {
|
||||
switch(sectionDTO.type)
|
||||
{
|
||||
switch (sectionDTO.type) {
|
||||
case SectionType.Map:
|
||||
(sectionDetailDTO as MapDTO).id = sectionDTO.id;
|
||||
(sectionDetailDTO as MapDTO).order = sectionDTO.order;
|
||||
(sectionDetailDTO as MapDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as MapDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as MapDTO).label = sectionDTO.label;
|
||||
@ -561,7 +603,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as SliderDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as SliderDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as SliderDTO).label = sectionDTO.label;
|
||||
@ -581,7 +624,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as VideoDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as VideoDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as VideoDTO).label = sectionDTO.label;
|
||||
@ -601,7 +645,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as WebDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as WebDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as WebDTO).label = sectionDTO.label;
|
||||
@ -621,7 +666,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as MenuDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as MenuDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as MenuDTO).label = sectionDTO.label;
|
||||
@ -641,7 +687,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as QuizDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as QuizDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as QuizDTO).label = sectionDTO.label;
|
||||
@ -661,7 +708,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as ArticleDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as ArticleDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as ArticleDTO).label = sectionDTO.label;
|
||||
@ -681,7 +729,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as PdfDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as PdfDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as PdfDTO).label = sectionDTO.label;
|
||||
@ -701,7 +750,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as GameDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as GameDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as GameDTO).label = sectionDTO.label;
|
||||
@ -721,7 +771,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as AgendaDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as AgendaDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as AgendaDTO).label = sectionDTO.label;
|
||||
@ -741,7 +792,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as WeatherDTO).dateCreation = sectionDTO.dateCreation;
|
||||
(sectionDetailDTO as WeatherDTO).type = sectionDTO.type;
|
||||
(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).parentId = sectionDTO.parentId;
|
||||
(sectionDetailDTO as WeatherDTO).label = sectionDTO.label;
|
||||
@ -755,24 +807,52 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
(sectionDetailDTO as WeatherDTO).longitude = sectionDTO.longitude;
|
||||
(sectionDetailDTO as WeatherDTO).meterZoneGPS = sectionDTO.meterZoneGPS;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<Object?> getSection(String sectionId, Client client) async {
|
||||
try{
|
||||
try {
|
||||
Object? section = await client.sectionApi!.sectionGetDetail(sectionId);
|
||||
return section;
|
||||
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
print(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uint8List?> _captureAndSharePng(GlobalKey globalKey, String sectionId) async {
|
||||
Future<Uint8List?> _captureAndSharePng(
|
||||
GlobalKey globalKey, String sectionId) async {
|
||||
try {
|
||||
RenderRepaintBoundary ? boundary = globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
||||
RenderRepaintBoundary? boundary =
|
||||
globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
||||
var image = await boundary.toImage();
|
||||
ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
|
||||
Uint8List pngBytes = byteData!.buffer.asUint8List();
|
||||
@ -782,8 +862,7 @@ Future<Uint8List?> _captureAndSharePng(GlobalKey globalKey, String sectionId) as
|
||||
a.click();
|
||||
|
||||
return pngBytes;
|
||||
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -8,20 +8,23 @@ import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.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.label = "";
|
||||
sectionDTO.configurationId = configurationId;
|
||||
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;
|
||||
sectionDTO.type = SectionType.Map;
|
||||
|
||||
var section = showDialog<SectionDTO?>(
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20.0))
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(20.0))),
|
||||
content: SingleChildScrollView(
|
||||
child: SizedBox(
|
||||
width: 750,
|
||||
@ -30,7 +33,12 @@ Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext
|
||||
constraints: BoxConstraints(minHeight: 300, minWidth: 300),
|
||||
child: Column(
|
||||
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(
|
||||
children: [
|
||||
SizedBox(
|
||||
@ -45,7 +53,13 @@ Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext
|
||||
),
|
||||
DropDownInputContainer(
|
||||
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",
|
||||
onChange: (String? value) {
|
||||
sectionDTO.type = SectionType.fromJson(value);
|
||||
@ -99,12 +113,18 @@ Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
press: () {
|
||||
if(sectionDTO.label != null && sectionDTO.label!.length > 2) {
|
||||
if (sectionDTO.label != null &&
|
||||
sectionDTO.label!.length > 2) {
|
||||
//onYes();
|
||||
Navigator.of(context).pop(sectionDTO);
|
||||
//create(sectionDTO, appContext, context, isSubSection, sendSubSection);
|
||||
} 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,
|
||||
@ -114,19 +134,21 @@ Future<SectionDTO?> showNewSection(String configurationId, AppContext appContext
|
||||
],
|
||||
),
|
||||
],
|
||||
), context: contextBuild
|
||||
);
|
||||
),
|
||||
context: contextBuild);
|
||||
|
||||
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) {
|
||||
ManagerAppContext managerAppContext = appContext.getContext();
|
||||
sectionDTO.instanceId = managerAppContext.instanceId;
|
||||
sectionDTO.isBeacon = false;
|
||||
sectionDTO.dateCreation = DateTime.now();
|
||||
SectionDTO? newSection = await managerAppContext.clientAPI!.sectionApi!.sectionCreate(sectionDTO);
|
||||
SectionDTO? newSection = await managerAppContext.clientAPI!.sectionApi!
|
||||
.sectionCreate(sectionDTO);
|
||||
|
||||
if (!isSubSection) {
|
||||
/*if (managerAppContext.selectedConfiguration.sectionIds == null) {
|
||||
@ -134,7 +156,8 @@ void create(SectionDTO sectionDTO, AppContext appContext, BuildContext context,
|
||||
}
|
||||
managerAppContext.selectedConfiguration.sectionIds.add(newSection.id);*/
|
||||
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 {
|
||||
sendSubSection!(newSection);
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@ class GameDTO {
|
||||
this.rows,
|
||||
this.cols,
|
||||
this.gameType,
|
||||
this.guidedPaths = const [],
|
||||
});
|
||||
|
||||
String? id;
|
||||
@ -135,6 +136,8 @@ class GameDTO {
|
||||
///
|
||||
GameTypes? gameType;
|
||||
|
||||
List<GuidedPathDTO>? guidedPaths;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
@ -164,7 +167,8 @@ class GameDTO {
|
||||
other.puzzleImageId == puzzleImageId &&
|
||||
other.rows == rows &&
|
||||
other.cols == cols &&
|
||||
other.gameType == gameType;
|
||||
other.gameType == gameType &&
|
||||
_deepEquality.equals(other.guidedPaths, guidedPaths);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
@ -194,11 +198,12 @@ class GameDTO {
|
||||
(puzzleImageId == null ? 0 : puzzleImageId!.hashCode) +
|
||||
(rows == null ? 0 : rows!.hashCode) +
|
||||
(cols == null ? 0 : cols!.hashCode) +
|
||||
(gameType == null ? 0 : gameType!.hashCode);
|
||||
(gameType == null ? 0 : gameType!.hashCode) +
|
||||
(guidedPaths == null ? 0 : guidedPaths!.hashCode);
|
||||
|
||||
@override
|
||||
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() {
|
||||
final json = <String, dynamic>{};
|
||||
@ -213,12 +218,12 @@ class GameDTO {
|
||||
json[r'label'] = null;
|
||||
}
|
||||
if (this.title != null) {
|
||||
json[r'title'] = this.title;
|
||||
json[r'title'] = this.title!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'title'] = null;
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
json[r'description'] = this.description!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'description'] = null;
|
||||
}
|
||||
@ -298,12 +303,13 @@ class GameDTO {
|
||||
json[r'beaconId'] = null;
|
||||
}
|
||||
if (this.messageDebut != null) {
|
||||
json[r'messageDebut'] = this.messageDebut;
|
||||
json[r'messageDebut'] =
|
||||
this.messageDebut!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'messageDebut'] = null;
|
||||
}
|
||||
if (this.messageFin != null) {
|
||||
json[r'messageFin'] = this.messageFin;
|
||||
json[r'messageFin'] = this.messageFin!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'messageFin'] = null;
|
||||
}
|
||||
@ -332,6 +338,11 @@ class GameDTO {
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -383,6 +394,7 @@ class GameDTO {
|
||||
rows: mapValueOfType<int>(json, r'rows'),
|
||||
cols: mapValueOfType<int>(json, r'cols'),
|
||||
gameType: GameTypes.fromJson(json[r'gameType']),
|
||||
guidedPaths: GuidedPathDTO.listFromJson(json[r'guidedPaths']),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@ -19,6 +19,7 @@ class GuidedPathDTO {
|
||||
this.description = const [],
|
||||
this.sectionMapId,
|
||||
this.sectionEventId,
|
||||
this.sectionGameId,
|
||||
this.isLinear,
|
||||
this.requireSuccessToAdvance,
|
||||
this.hideNextStepsUntilComplete,
|
||||
@ -38,6 +39,8 @@ class GuidedPathDTO {
|
||||
|
||||
String? sectionEventId;
|
||||
|
||||
String? sectionGameId;
|
||||
|
||||
///
|
||||
/// 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
|
||||
@ -82,6 +85,7 @@ class GuidedPathDTO {
|
||||
_deepEquality.equals(other.description, description) &&
|
||||
other.sectionMapId == sectionMapId &&
|
||||
other.sectionEventId == sectionEventId &&
|
||||
other.sectionGameId == sectionGameId &&
|
||||
other.isLinear == isLinear &&
|
||||
other.requireSuccessToAdvance == requireSuccessToAdvance &&
|
||||
other.hideNextStepsUntilComplete == hideNextStepsUntilComplete &&
|
||||
@ -97,6 +101,7 @@ class GuidedPathDTO {
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(sectionMapId == null ? 0 : sectionMapId!.hashCode) +
|
||||
(sectionEventId == null ? 0 : sectionEventId!.hashCode) +
|
||||
(sectionGameId == null ? 0 : sectionGameId!.hashCode) +
|
||||
(isLinear == null ? 0 : isLinear!.hashCode) +
|
||||
(requireSuccessToAdvance == null
|
||||
? 0
|
||||
@ -109,7 +114,7 @@ class GuidedPathDTO {
|
||||
|
||||
@override
|
||||
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() {
|
||||
final json = <String, dynamic>{};
|
||||
@ -124,12 +129,12 @@ class GuidedPathDTO {
|
||||
json[r'instanceId'] = null;
|
||||
}
|
||||
if (this.title != null) {
|
||||
json[r'title'] = this.title;
|
||||
json[r'title'] = this.title!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'title'] = null;
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
json[r'description'] = this.description!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'description'] = null;
|
||||
}
|
||||
@ -143,6 +148,11 @@ class GuidedPathDTO {
|
||||
} else {
|
||||
json[r'sectionEventId'] = null;
|
||||
}
|
||||
if (this.sectionGameId != null) {
|
||||
json[r'sectionGameId'] = this.sectionGameId;
|
||||
} else {
|
||||
json[r'sectionGameId'] = null;
|
||||
}
|
||||
if (this.isLinear != null) {
|
||||
json[r'isLinear'] = this.isLinear;
|
||||
} else {
|
||||
@ -164,7 +174,7 @@ class GuidedPathDTO {
|
||||
json[r'order'] = null;
|
||||
}
|
||||
if (this.steps != null) {
|
||||
json[r'steps'] = this.steps;
|
||||
json[r'steps'] = this.steps!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'steps'] = null;
|
||||
}
|
||||
@ -198,6 +208,7 @@ class GuidedPathDTO {
|
||||
description: TranslationDTO.listFromJson(json[r'description']),
|
||||
sectionMapId: mapValueOfType<String>(json, r'sectionMapId'),
|
||||
sectionEventId: mapValueOfType<String>(json, r'sectionEventId'),
|
||||
sectionGameId: mapValueOfType<String>(json, r'sectionGameId'),
|
||||
isLinear: mapValueOfType<bool>(json, r'isLinear'),
|
||||
requireSuccessToAdvance:
|
||||
mapValueOfType<bool>(json, r'requireSuccessToAdvance'),
|
||||
|
||||
@ -47,7 +47,7 @@ class GuidedStepDTO {
|
||||
|
||||
List<TranslationDTO>? description;
|
||||
|
||||
EventAddressDTOGeometry? geometry;
|
||||
GeometryDTO? geometry;
|
||||
|
||||
double? zoneRadiusMeters;
|
||||
|
||||
@ -151,12 +151,12 @@ class GuidedStepDTO {
|
||||
json[r'order'] = null;
|
||||
}
|
||||
if (this.title != null) {
|
||||
json[r'title'] = this.title;
|
||||
json[r'title'] = this.title!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'title'] = null;
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
json[r'description'] = this.description!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'description'] = null;
|
||||
}
|
||||
@ -206,12 +206,14 @@ class GuidedStepDTO {
|
||||
json[r'timerSeconds'] = null;
|
||||
}
|
||||
if (this.timerExpiredMessage != null) {
|
||||
json[r'timerExpiredMessage'] = this.timerExpiredMessage;
|
||||
json[r'timerExpiredMessage'] =
|
||||
this.timerExpiredMessage!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'timerExpiredMessage'] = null;
|
||||
}
|
||||
if (this.quizQuestions != null) {
|
||||
json[r'quizQuestions'] = this.quizQuestions;
|
||||
json[r'quizQuestions'] =
|
||||
this.quizQuestions!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'quizQuestions'] = null;
|
||||
}
|
||||
@ -244,7 +246,7 @@ class GuidedStepDTO {
|
||||
order: mapValueOfType<int>(json, r'order'),
|
||||
title: TranslationDTO.listFromJson(json[r'title']),
|
||||
description: TranslationDTO.listFromJson(json[r'description']),
|
||||
geometry: EventAddressDTOGeometry.fromJson(json[r'geometry']),
|
||||
geometry: GeometryDTO.fromJson(json[r'geometry']),
|
||||
zoneRadiusMeters: mapValueOfType<double>(json, r'zoneRadiusMeters'),
|
||||
imageUrl: mapValueOfType<String>(json, r'imageUrl'),
|
||||
triggerGeoPointId: mapValueOfType<int>(json, r'triggerGeoPointId'),
|
||||
|
||||
@ -42,6 +42,8 @@ class MapDTO {
|
||||
this.categories = const [],
|
||||
this.centerLatitude,
|
||||
this.centerLongitude,
|
||||
this.isParcours,
|
||||
this.guidedPaths = const [],
|
||||
});
|
||||
|
||||
String? id;
|
||||
@ -132,6 +134,10 @@ class MapDTO {
|
||||
|
||||
String? centerLongitude;
|
||||
|
||||
bool? isParcours;
|
||||
|
||||
List<GuidedPathDTO>? guidedPaths;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
@ -164,7 +170,9 @@ class MapDTO {
|
||||
other.iconSource == iconSource &&
|
||||
_deepEquality.equals(other.categories, categories) &&
|
||||
other.centerLatitude == centerLatitude &&
|
||||
other.centerLongitude == centerLongitude;
|
||||
other.centerLongitude == centerLongitude &&
|
||||
other.isParcours == isParcours &&
|
||||
_deepEquality.equals(other.guidedPaths, guidedPaths);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
@ -197,11 +205,13 @@ class MapDTO {
|
||||
(iconSource == null ? 0 : iconSource!.hashCode) +
|
||||
(categories == null ? 0 : categories!.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
|
||||
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() {
|
||||
final json = <String, dynamic>{};
|
||||
@ -216,12 +226,12 @@ class MapDTO {
|
||||
json[r'label'] = null;
|
||||
}
|
||||
if (this.title != null) {
|
||||
json[r'title'] = this.title;
|
||||
json[r'title'] = this.title!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'title'] = null;
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
json[r'description'] = this.description!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'description'] = null;
|
||||
}
|
||||
@ -321,7 +331,7 @@ class MapDTO {
|
||||
json[r'mapProvider'] = null;
|
||||
}
|
||||
if (this.points != null) {
|
||||
json[r'points'] = this.points;
|
||||
json[r'points'] = this.points!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'points'] = null;
|
||||
}
|
||||
@ -336,7 +346,7 @@ class MapDTO {
|
||||
json[r'iconSource'] = null;
|
||||
}
|
||||
if (this.categories != null) {
|
||||
json[r'categories'] = this.categories;
|
||||
json[r'categories'] = this.categories!.map((v) => v.toJson()).toList();
|
||||
} else {
|
||||
json[r'categories'] = null;
|
||||
}
|
||||
@ -350,6 +360,16 @@ class MapDTO {
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -403,6 +423,8 @@ class MapDTO {
|
||||
categories: CategorieDTO.listFromJson(json[r'categories']),
|
||||
centerLatitude: mapValueOfType<String>(json, r'centerLatitude'),
|
||||
centerLongitude: mapValueOfType<String>(json, r'centerLongitude'),
|
||||
isParcours: mapValueOfType<bool>(json, r'isParcours'),
|
||||
guidedPaths: GuidedPathDTO.listFromJson(json[r'guidedPaths']),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@ -447,7 +447,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
flutter_map:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_map
|
||||
sha256: "87cc8349b8fa5dccda5af50018c7374b6645334a0d680931c1fe11bce88fa5bb"
|
||||
@ -737,7 +737,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.4.13"
|
||||
latlong2:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: latlong2
|
||||
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
|
||||
|
||||
@ -49,6 +49,8 @@ dependencies:
|
||||
multi_select_flutter: ^4.1.3
|
||||
youtube_player_iframe: ^5.0.0
|
||||
location_picker_flutter_map: ^3.0.1
|
||||
flutter_map: ^6.2.1
|
||||
latlong2: ^0.9.1
|
||||
go_router: ^16.2.0
|
||||
|
||||
#msix: ^2.1.3
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user