Applications -> Mobile Done !

This commit is contained in:
Thomas Fransolet 2025-10-17 23:22:36 +02:00
parent 61b3289e35
commit 38c36adc8a
2 changed files with 354 additions and 350 deletions

View File

@ -11,6 +11,8 @@ class SingleChoiceInputContainer<T> extends StatefulWidget {
final double borderRadius;
final Color selectedColor;
final Color textColor;
final String Function(T) valueExtractor;
final String Function(T) labelExtractor;
const SingleChoiceInputContainer({
Key? key,
@ -19,6 +21,8 @@ class SingleChoiceInputContainer<T> extends StatefulWidget {
required this.selected,
required this.values,
required this.onChanged,
required this.valueExtractor,
required this.labelExtractor,
this.borderRadius = 12,
this.selectedColor = kPrimaryColor,
this.textColor = kWhite,
@ -41,6 +45,14 @@ class _SingleChoiceInputContainerState<T>
@override
Widget build(BuildContext context) {
// Protection contre les valeurs non présentes dans la liste
String? selectedValue;
if (_selected != null) {
final exists = widget.values
.any((v) => widget.valueExtractor(v) == widget.valueExtractor(_selected!));
selectedValue = exists ? widget.valueExtractor(_selected!) : null;
}
return FormField<T?>(
initialValue: _selected,
builder: (state) {
@ -60,42 +72,35 @@ class _SingleChoiceInputContainerState<T>
width: 225,
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 16),
/*decoration: BoxDecoration(
borderRadius: BorderRadius.circular(widget.borderRadius),
border: Border.all(color: Colors.grey.shade400, width: 1.2),
color: Colors.white,
),*/
child: DropdownButton<T>(
underline: const SizedBox(
child: Divider(height: 0, thickness: 1.5, color: kPrimaryColor),
),
focusColor: Colors.transparent,
icon: const Icon(Icons.arrow_drop_down, color: kPrimaryColor),
child: DropdownButton<String>(
value: selectedValue,
hint: Text(widget.selectLabel,
style: TextStyle(color: Colors.grey.shade600)),
isExpanded: true,
value: _selected,
hint: Text(
widget.selectLabel,
style: TextStyle(color: Colors.grey.shade600),
),
items: widget.values.map((v) {
return DropdownMenuItem<T>(
value: v,
return DropdownMenuItem<String>(
value: widget.valueExtractor(v),
child: Text(
(v as SectionEventDTO).label ?? "",
widget.labelExtractor(v),
style: const TextStyle(fontSize: 14),
),
);
}).toList(),
onChanged: (value) {
onChanged: widget.values.isEmpty
? null
: (id) {
T? selected = widget.values
.cast<T?>()
.firstWhere(
(v) => widget.valueExtractor(v!) == id,
orElse: () => null,
);
setState(() {
if (value is SectionEventDTO && value.id == null) {
_selected = null; // affiche le hint
} else {
_selected = value;
}
_selected = selected;
});
widget.onChanged(_selected);
state.didChange(_selected);
widget.onChanged(selected);
state.didChange(selected);
},
),
),

View File

@ -29,6 +29,14 @@ class AppConfigurationLinkScreen extends StatefulWidget {
}
class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen> {
late ApplicationInstanceDTO _applicationInstanceDTO;
@override
void initState() {
super.initState();
_applicationInstanceDTO = widget.applicationInstanceDTO;
}
@override
Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context);
@ -68,25 +76,25 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
child: Center(
child: ResourceInputContainer(
label: "Image principale :",
initialValue: widget.applicationInstanceDTO.mainImageId,
initialValue: _applicationInstanceDTO.mainImageId,
color: kPrimaryColor,
imageFit: BoxFit.fitHeight,
onChanged: (ResourceDTO resource) async {
if(resource.id == null) {
widget.applicationInstanceDTO.mainImageId = null;
widget.applicationInstanceDTO.mainImageUrl = null;
_applicationInstanceDTO.mainImageId = null;
_applicationInstanceDTO.mainImageUrl = null;
} else {
widget.applicationInstanceDTO.mainImageId = resource.id;
widget.applicationInstanceDTO.mainImageUrl = resource.url;
_applicationInstanceDTO.mainImageId = resource.id;
_applicationInstanceDTO.mainImageUrl = resource.url;
}
// automatic save
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) {
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
setState(() {
/*setState(() {
});
});*/
}
},
),
@ -99,25 +107,25 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
child: Center(
child: ResourceInputContainer(
label: "Loader :",
initialValue: widget.applicationInstanceDTO.loaderImageId,
initialValue: _applicationInstanceDTO.loaderImageId,
color: kPrimaryColor,
imageFit: BoxFit.fitHeight,
onChanged: (ResourceDTO resource) async {
if(resource.id == null) {
widget.applicationInstanceDTO.loaderImageId = null;
widget.applicationInstanceDTO.loaderImageUrl = null;
_applicationInstanceDTO.loaderImageId = null;
_applicationInstanceDTO.loaderImageUrl = null;
} else {
widget.applicationInstanceDTO.loaderImageId = resource.id;
widget.applicationInstanceDTO.loaderImageUrl = resource.url;
_applicationInstanceDTO.loaderImageId = resource.id;
_applicationInstanceDTO.loaderImageUrl = resource.url;
}
// automatic save
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) {
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
setState(() {
/*setState(() {
});
});*/
}
},
),
@ -131,16 +139,16 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
child: ColorPickerInputContainer(
label: "Couleur principale :",
fontSize: 20,
color: widget.applicationInstanceDTO.primaryColor,
color: _applicationInstanceDTO.primaryColor,
onChanged: (value) async {
widget.applicationInstanceDTO.primaryColor = value;
_applicationInstanceDTO.primaryColor = value;
// automatic save
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) {
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
setState(() {
/*setState(() {
});
});*/
}
},
),
@ -154,16 +162,16 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
child: ColorPickerInputContainer(
label: "Couleur secondaire :",
fontSize: 20,
color: widget.applicationInstanceDTO.secondaryColor,
color: _applicationInstanceDTO.secondaryColor,
onChanged: (value) async {
widget.applicationInstanceDTO.secondaryColor = value;
_applicationInstanceDTO.secondaryColor = value;
// automatic save
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) {
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
setState(() {
/*setState(() {
});
});*/
}
},
),
@ -176,19 +184,19 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
child: Center(
child: SegmentedEnumInputContainer(
label: "Affichage :",
selected: LayoutMainPageType.MasonryGrid,
selected: _applicationInstanceDTO.layoutMainPage,
values: LayoutMainPageType.values,
inputValues: { LayoutMainPageType.SimpleGrid: {'label': 'Grille', 'icon': Icons.grid_view}, LayoutMainPageType.MasonryGrid : {'label': 'Masonry', 'icon': Icons.view_quilt }},
onChanged: (value) async {
var tempOutput = value;
widget.applicationInstanceDTO.layoutMainPage = tempOutput;
_applicationInstanceDTO.layoutMainPage = tempOutput;
// automatic save
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) {
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
setState(() {
/*setState(() {
});
});*/
}
//print(configurationDTO.languages);
},
@ -202,21 +210,21 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
child: Center(
child: MultiSelectDropdownLanguageContainer(
label: "Langues :",
initialValue: widget.applicationInstanceDTO.languages != null ? widget.applicationInstanceDTO.languages!: [],
initialValue: _applicationInstanceDTO.languages != null ? _applicationInstanceDTO.languages!: [],
values: languages,
isMultiple: true,
fontSize: 20,
isAtLeastOne: true,
onChanged: (value) async {
var tempOutput = new List<String>.from(value);
widget.applicationInstanceDTO.languages = tempOutput;
_applicationInstanceDTO.languages = tempOutput;
// automatic save
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) {
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
setState(() {
/*setState(() {
});
});*/
}
//print(configurationDTO.languages);
},
@ -229,7 +237,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
height: elementHeight,
child: Center(
child: FutureBuilder(
future: getSectionEvents(appContext, widget.applicationInstanceDTO),
future: getSectionEvents(appContext, _applicationInstanceDTO),
builder: (context, snapshot) {
var rawList = snapshot.data;
var rawSubsections = jsonDecode(jsonEncode(snapshot.data));
@ -239,29 +247,37 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
sectionEvents.add(SectionEventDTO(id: null, label: "Aucun"));
print(_applicationInstanceDTO.sectionEventId);
print(_applicationInstanceDTO.sectionEventDTO);
return SingleChoiceInputContainer<SectionEventDTO?>(
label: "Evènement à l'affiche :",
selectLabel: "Choisir un évènement",
selected: widget.applicationInstanceDTO.sectionEventDTO,
selected: _applicationInstanceDTO.sectionEventDTO,
values: sectionEvents.toList(),
valueExtractor: (SectionEventDTO? dto) => dto?.id ?? "",
labelExtractor: (SectionEventDTO? dto) => dto?.label ?? "Aucun",
onChanged: (SectionEventDTO? sectionEvent) async {
if(sectionEvent == null) {
widget.applicationInstanceDTO.sectionEventId = null;
widget.applicationInstanceDTO.sectionEventDTO = null;
_applicationInstanceDTO.sectionEventId = null;
_applicationInstanceDTO.sectionEventDTO = null;
return;
}
print("Sélectionné: $sectionEvent");
print(sectionEvent.label);
print(sectionEvent.id);
widget.applicationInstanceDTO.sectionEventId = sectionEvent.id;
_applicationInstanceDTO.sectionEventId = sectionEvent.id;
print(_applicationInstanceDTO.sectionEventId);
// automatic save
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) {
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
setState(() {
});
//setState(() {
_applicationInstanceDTO.sectionEventDTO = applicationLink.sectionEventDTO;
//_applicationInstanceDTO = applicationLink;
//});
}
},
);
@ -285,7 +301,9 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
margin: const EdgeInsets.symmetric(vertical: 8),
color: kWhite,
elevation: 0,
child: Stack(
child: StatefulBuilder(
builder: (context, localSetState) {
return Stack(
//crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
@ -305,6 +323,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
//color: Colors.blue,
child: SingleChildScrollView(
child: ReorderableCustomList<AppConfigurationLinkDTO>(
key: ValueKey(appConfigurationLinks),
items: appConfigurationLinks,
shrinkWrap: true,
onChanged: (updatedList) async {
@ -316,40 +335,10 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
}
// TODO use order put method
var result = await updateAppConfigurationOrder(appContext, updatedList);
setState(() {
// for refresh
localSetState(() {});
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
});
},
actions: [
/*(BuildContext context, int index, AppConfigurationLinkDTO link) {
return Container(
height: 50,
width: 50,
child: InkWell(
onTap: () async {
try {
var applicationInstance = widget.applicationInstanceDTO;
applicationInstance.
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
if(applicationLink != null) {
if(newValue) {
showNotification(kSuccess, kWhite, "Configuration activée avec succès", context, null);
} else {
showNotification(kSuccess, kWhite, "Configuration désactivée avec succès", context, null);
}
setState(() {
link.isActive = applicationLink.isActive;
});
}
} catch (e) {
showNotification(kError, kWhite, "Une erreur est survenue", context, null);
}
},
child: Icon(Icons.star, color: kError, size: 25),
),
);
},*/
(BuildContext context, int index, AppConfigurationLinkDTO link) {
return Container(
height: 50,
@ -370,7 +359,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
} else {
showNotification(kSuccess, kWhite, "Configuration désactivée avec succès", context, null);
}
setState(() {
localSetState(() {
link.isActive = applicationLink.isActive;
});
}
@ -392,7 +381,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
() {},
() async {
try {
var result = await deleteConfigurationToApp(appContext, link, widget.applicationInstanceDTO);
var result = await deleteConfigurationToApp(appContext, link, _applicationInstanceDTO);
showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null);
@ -414,9 +403,8 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
padding: const EdgeInsets.all(8),
itemBuilder: (context, index, appConfigurationLink) {
return Container(
decoration: BoxDecoration(
),
key: ValueKey(appConfigurationLink.id),
decoration: BoxDecoration(),
margin: const EdgeInsets.symmetric(vertical: 3),
padding: const EdgeInsets.all(8),
child: Row(
@ -475,7 +463,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
() {},
() async {
try {
var result = await deleteConfigurationToApp(appContext, appConfigurationLink, widget.applicationInstanceDTO);
var result = await deleteConfigurationToApp(appContext, appConfigurationLink, _applicationInstanceDTO);
showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null);
@ -510,7 +498,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
if(result != null) {
for(var configurationId in result) {
AppConfigurationLinkDTO appConfigurationLinkDTO = AppConfigurationLinkDTO(
applicationInstanceId: widget.applicationInstanceDTO.id,
applicationInstanceId: _applicationInstanceDTO.id,
configurationId: configurationId,
isActive: true,
isDate: false,
@ -518,7 +506,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
isSectionImageBackground: false,
layoutMainPage: LayoutMainPageType.SimpleGrid
);
await addConfigurationToApp(appContext, appConfigurationLinkDTO, widget.applicationInstanceDTO);
await addConfigurationToApp(appContext, appConfigurationLinkDTO, _applicationInstanceDTO);
}
setState(() {
// Refresh ui
@ -540,12 +528,14 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
),
) : SizedBox(),
],
);
}
),
);
}
return FutureBuilder(
future: getAppConfigurationLink(appContext, widget.applicationInstanceDTO),
future: getAppConfigurationLink(appContext, _applicationInstanceDTO),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
List<AppConfigurationLinkDTO>? appConfigurationLinks = snapshot.data;
@ -614,7 +604,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
label: "Titre affiché:",
modalLabel: "Titre",
color: kPrimaryColor,
initialValue: widget.applicationInstanceDTO.title,
initialValue: _applicationInstanceDTO.title,
onGetResult: (value) {
if (sectionDTO.title! != value) {
sectionDTO.title = value;
@ -628,30 +618,30 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// Image principale
ResourceInputContainer(
label: "Image principale :",
initialValue: widget.applicationInstanceDTO.mainImageId,
initialValue: _applicationInstanceDTO.mainImageId,
color: kPrimaryColor,
onChanged: (ResourceDTO resource) {
if(resource.id == null) {
widget.applicationInstanceDTO.mainImageId = null;
widget.applicationInstanceDTO.mainImageUrl = null;
_applicationInstanceDTO.mainImageId = null;
_applicationInstanceDTO.mainImageUrl = null;
} else {
widget.applicationInstanceDTO.mainImageId = resource.id;
widget.applicationInstanceDTO.mainImageUrl = resource.url;
_applicationInstanceDTO.mainImageId = resource.id;
_applicationInstanceDTO.mainImageUrl = resource.url;
}
},
),
// Image Loader
ResourceInputContainer(
label: "Loader :",
initialValue: widget.applicationInstanceDTO.loaderImageId,
initialValue: _applicationInstanceDTO.loaderImageId,
color: kPrimaryColor,
onChanged: (ResourceDTO resource) {
if(resource.id == null) {
widget.applicationInstanceDTO.loaderImageId = null;
widget.applicationInstanceDTO.loaderImageUrl = null;
_applicationInstanceDTO.loaderImageId = null;
_applicationInstanceDTO.loaderImageUrl = null;
} else {
widget.applicationInstanceDTO.loaderImageId = resource.id;
widget.applicationInstanceDTO.loaderImageUrl = resource.url;
_applicationInstanceDTO.loaderImageId = resource.id;
_applicationInstanceDTO.loaderImageUrl = resource.url;
}
},
),
@ -659,18 +649,18 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
ColorPickerInputContainer(
label: "Couleur principale :",
fontSize: 20,
color: widget.applicationInstanceDTO.primaryColor,
color: _applicationInstanceDTO.primaryColor,
onChanged: (value) {
widget.applicationInstanceDTO.primaryColor = value;
_applicationInstanceDTO.primaryColor = value;
},
),
// Secondary color
ColorPickerInputContainer(
label: "Couleur secondaire :",
fontSize: 20,
color: widget.applicationInstanceDTO.secondaryColor,
color: _applicationInstanceDTO.secondaryColor,
onChanged: (value) {
widget.applicationInstanceDTO.secondaryColor = value;
_applicationInstanceDTO.secondaryColor = value;
},
),
// Layout (Grid or Mansonry)
@ -678,14 +668,14 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// Langues
MultiSelectDropdownLanguageContainer(
label: "Langues :",
initialValue: widget.applicationInstanceDTO.languages != null ? widget.applicationInstanceDTO.languages!: [],
initialValue: _applicationInstanceDTO.languages != null ? _applicationInstanceDTO.languages!: [],
values: languages,
isMultiple: true,
fontSize: 20,
isAtLeastOne: true,
onChanged: (value) {
var tempOutput = new List<String>.from(value);
widget.applicationInstanceDTO.languages = tempOutput;
_applicationInstanceDTO.languages = tempOutput;
//print(configurationDTO.languages);
},
),
@ -740,9 +730,9 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
child: InkWell(
onTap: () async {
try {
var applicationInstance = widget.applicationInstanceDTO;
var applicationInstance = _applicationInstanceDTO;
applicationInstance.
var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO);
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) {
if(newValue) {
showNotification(kSuccess, kWhite, "Configuration activée avec succès", context, null);
@ -803,7 +793,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
() {},
() async {
try {
var result = await deleteConfigurationToApp(appContext, link, widget.applicationInstanceDTO);
var result = await deleteConfigurationToApp(appContext, link, _applicationInstanceDTO);
showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null);
@ -865,7 +855,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
if(result != null) {
for(var configurationId in result) {
AppConfigurationLinkDTO appConfigurationLinkDTO = AppConfigurationLinkDTO(
applicationInstanceId: widget.applicationInstanceDTO.id,
applicationInstanceId: _applicationInstanceDTO.id,
configurationId: configurationId,
isActive: true,
isDate: false,
@ -873,7 +863,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
isSectionImageBackground: false,
layoutMainPage: LayoutMainPageType.SimpleGrid
);
await addConfigurationToApp(appContext, appConfigurationLinkDTO, widget.applicationInstanceDTO);
await addConfigurationToApp(appContext, appConfigurationLinkDTO, _applicationInstanceDTO);
}
setState(() {
// Refresh ui
@ -924,7 +914,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
() {},
() async {
try {
var result = await deleteConfigurationToApp(appContext, appConfigurationLink, widget.applicationInstanceDTO);
var result = await deleteConfigurationToApp(appContext, appConfigurationLink, _applicationInstanceDTO);
showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null);
@ -1013,7 +1003,16 @@ Future<AppConfigurationLinkDTO?> addConfigurationToApp(AppContext appContext, Ap
}
Future<ApplicationInstanceDTO?> updateApplicationInstance(AppContext appContext, ApplicationInstanceDTO applicationInstanceDTO) async {
ApplicationInstanceDTO? result = await (appContext.getContext() as ManagerAppContext).clientAPI!.applicationInstanceApi!.applicationInstanceUpdate(applicationInstanceDTO);
var applicationInstanceToSend = ApplicationInstanceDTO.fromJson(
applicationInstanceDTO.toJson()
);
applicationInstanceToSend?.sectionEventDTO = null;
applicationInstanceToSend?.sectionEventId = applicationInstanceDTO.sectionEventId;
applicationInstanceToSend?.appType = applicationInstanceDTO.appType;
applicationInstanceToSend?.layoutMainPage = applicationInstanceDTO.layoutMainPage;
ApplicationInstanceDTO? result = await (appContext.getContext() as ManagerAppContext).clientAPI!.applicationInstanceApi!.applicationInstanceUpdate(applicationInstanceToSend!);
return result;
}