diff --git a/assets/images/MyInfoMate_logo_only.png b/assets/images/MyInfoMate_logo_only.png new file mode 100644 index 0000000..28082f3 Binary files /dev/null and b/assets/images/MyInfoMate_logo_only.png differ diff --git a/assets/images/MyInfoMate_logo_only.svg b/assets/images/MyInfoMate_logo_only.svg new file mode 100644 index 0000000..84990a1 --- /dev/null +++ b/assets/images/MyInfoMate_logo_only.svg @@ -0,0 +1,650 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/Components/color_picker_input_container.dart b/lib/Components/color_picker_input_container.dart index de8f8c9..d8593fe 100644 --- a/lib/Components/color_picker_input_container.dart +++ b/lib/Components/color_picker_input_container.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:manager_app/Components/color_picker.dart'; +import 'package:manager_app/constants.dart'; class ColorPickerInputContainer extends StatefulWidget { final String? color; @@ -16,7 +17,8 @@ class ColorPickerInputContainer extends StatefulWidget { }) : super(key: key); @override - _ColorPickerInputContainerState createState() => _ColorPickerInputContainerState(); + _ColorPickerInputContainerState createState() => + _ColorPickerInputContainerState(); } class _ColorPickerInputContainerState extends State { @@ -28,7 +30,8 @@ class _ColorPickerInputContainerState extends State { try { colorVar = widget.color == null || widget.color!.isEmpty ? Colors.grey - : Color(int.parse(widget.color!.split('(0x')[1].split(')')[0], radix: 16)); + : Color(int.parse( + widget.color!.split('(0x')[1].split(')')[0], radix: 16)); } catch (e) { colorVar = Colors.grey; } @@ -36,15 +39,28 @@ class _ColorPickerInputContainerState extends State { @override Widget build(BuildContext context) { - return FormField( - initialValue: colorVar, - builder: (state) { - return InputDecorator( - decoration: InputDecoration( - labelText: widget.label, - border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), - contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 20), + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Align( + alignment: AlignmentDirectional.centerStart, + child: Text( + widget.label, + style: const TextStyle( + fontWeight: FontWeight.w400, + fontSize: 16, + ), ), + ), + const SizedBox(height: 8, width: 10), + Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + /*decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade400, width: 1.2), + ),*/ child: InkWell( onTap: () { showColorPicker(colorVar, (Color color) { @@ -64,8 +80,8 @@ class _ColorPickerInputContainerState extends State { ), ), ), - ); - }, + ), + ], ); } diff --git a/lib/Components/common_loader.dart b/lib/Components/common_loader.dart index b81e538..a8568a0 100644 --- a/lib/Components/common_loader.dart +++ b/lib/Components/common_loader.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:manager_app/constants.dart'; class CommonLoader extends StatefulWidget { @@ -44,7 +45,10 @@ class _CommonLoaderState extends State with TickerProviderStateMix return Center( child: RotationTransition( turns: Tween(begin: 0.0, end: 3.0).animate(_controller!), - child: Icon(Icons.museum_outlined, color: kPrimaryColor, size: widget.iconSize == null ? size.height*0.1 : widget.iconSize!), + child: SizedBox( + height: 45, + child: SvgPicture.asset('assets/images/MyInfoMate_logo_only.svg') + )/*Icon(Icons.museum_outlined, color: kPrimaryColor, size: widget.iconSize == null ? size.height*0.1 : widget.iconSize!)*/, ), ); } diff --git a/lib/Components/multi_select_dropdown_language_container.dart b/lib/Components/multi_select_dropdown_language_container.dart index 83f627d..250d42d 100644 --- a/lib/Components/multi_select_dropdown_language_container.dart +++ b/lib/Components/multi_select_dropdown_language_container.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:multi_select_flutter/multi_select_flutter.dart'; import 'package:manager_app/constants.dart'; -class MultiSelectDropdownLanguageContainer extends StatelessWidget { +class MultiSelectDropdownLanguageContainer extends StatefulWidget { final Color color; final String label; final String labelHint; @@ -27,57 +27,83 @@ class MultiSelectDropdownLanguageContainer extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) { - return FormField>( - initialValue: initialValue, - builder: (state) { - return InputDecorator( - decoration: InputDecoration( - labelText: label, - border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), - contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 20), - ), - child: Builder( - builder: (context) { - return MultiSelectDialogField( - items: values.map((e) => MultiSelectItem(e, e)).toList(), - listType: MultiSelectListType.LIST, - initialValue: state.value ?? [], - buttonText: Text( - (state.value == null || state.value!.isEmpty) - ? "Aucune sélection" - : _buildSummary(state.value!), - ), - title: Text(labelHint), - searchable: true, - selectedColor: kPrimaryColor, - checkColor: Colors.white, - chipDisplay: MultiSelectChipDisplay.none(), - dialogHeight: MediaQuery.of(context).size.height * 0.4, - dialogWidth: MediaQuery.of(context).size.width * 0.6, - onConfirm: (selected) { - if (isAtLeastOne && selected.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text("Au moins une valeur doit être sélectionnée"), - ), - ); - } else { - onChanged(selected.cast()); - state.didChange(selected); - } - }, - ); - }, - ), - ); - }, - ); + State createState() => + _MultiSelectDropdownLanguageContainerState(); +} + +class _MultiSelectDropdownLanguageContainerState + extends State { + late List _selectedValues; + + @override + void initState() { + super.initState(); + _selectedValues = List.from(widget.initialValue); } String _buildSummary(List selected) { - if (selected.isEmpty) return "Aucune sélection"; + if (selected.isEmpty) return widget.labelHint; if (selected.length <= 5) return selected.join(", "); return "${selected.length} sélectionnés"; } + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Align( + alignment: AlignmentDirectional.centerStart, + child: Text( + widget.label, + style: const TextStyle( + fontWeight: FontWeight.w400, + fontSize: 16, + ), + ), + ), + const SizedBox(height: 8, width: 10), + Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 35), + /*decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade400, width: 1.2), + color: Colors.white, + ),*/ + child: MultiSelectDialogField( + items: widget.values.map((e) => MultiSelectItem(e, e)).toList(), + initialValue: _selectedValues, + listType: MultiSelectListType.LIST, + searchable: true, + searchIcon: Icon(Icons.search, color: kPrimaryColor), + selectedColor: kPrimaryColor, + checkColor: Colors.white, + buttonIcon: Icon(Icons.arrow_drop_down, color: kPrimaryColor), + buttonText: Text( + _buildSummary(_selectedValues), + style: TextStyle(color: Colors.black87, fontSize: 14), + ), + chipDisplay: MultiSelectChipDisplay.none(), + dialogHeight: MediaQuery.of(context).size.height * 0.4, + dialogWidth: MediaQuery.of(context).size.width * 0.6, + onConfirm: (selected) { + if (widget.isAtLeastOne && selected.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("Au moins une valeur doit être sélectionnée"), + ), + ); + } else { + setState(() { + _selectedValues = selected.cast(); + }); + widget.onChanged(_selectedValues); + } + }, + ), + ), + ], + ); + } } diff --git a/lib/Components/resource_input_container.dart b/lib/Components/resource_input_container.dart index 904e456..e7052d8 100644 --- a/lib/Components/resource_input_container.dart +++ b/lib/Components/resource_input_container.dart @@ -39,8 +39,8 @@ class _ResourceInputContainerState extends State { @override void initState() { - resourceIdToShow = widget.initialValue; super.initState(); + resourceIdToShow = widget.initialValue; } @override @@ -51,15 +51,27 @@ class _ResourceInputContainerState extends State { resourceIdToShow = widget.initialValue; } - return FormField( - initialValue: resourceIdToShow, - builder: (state) { - return InputDecorator( - decoration: InputDecoration( - labelText: widget.label, - border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), - contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 20), + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Align( + alignment: AlignmentDirectional.centerStart, + child: Text( + widget.label, + style: const TextStyle( + fontWeight: FontWeight.w400, + fontSize: 16, + ), ), + ), + const SizedBox(height: 8, width: 10), + Container( + padding: const EdgeInsets.all(4), + /*decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade400, width: 1.2), + ),*/ child: InkWell( onTap: () async { ResourceDTO? result = await showSelectResourceModal( @@ -81,7 +93,7 @@ class _ResourceInputContainerState extends State { }, child: Container( height: widget.isSmall ? 35 : 100, - width: widget.isSmall ? 60 : double.infinity, + width: widget.isSmall ? 60 : 120, alignment: Alignment.center, decoration: BoxDecoration( color: resourceIdToShow == null ? widget.color : Colors.transparent, @@ -90,7 +102,10 @@ class _ResourceInputContainerState extends State { child: resourceIdToShow == null ? Text( "Choisir", - style: TextStyle(color: kWhite, fontSize: widget.fontSize), + style: TextStyle( + color: kWhite, + fontSize: widget.fontSize, + ), maxLines: 1, ) : FutureBuilder( @@ -100,7 +115,7 @@ class _ResourceInputContainerState extends State { .resourceGetDetail(resourceIdToShow!), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return SizedBox( + return const SizedBox( width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2), @@ -108,7 +123,10 @@ class _ResourceInputContainerState extends State { } else if (snapshot.hasError || snapshot.data == null) { return Text( "Erreur", - style: TextStyle(color: kWhite, fontSize: widget.fontSize), + style: TextStyle( + color: kWhite, + fontSize: widget.fontSize, + ), maxLines: 1, ); } else { @@ -126,8 +144,8 @@ class _ResourceInputContainerState extends State { ), ), ), - ); - }, + ), + ], ); } } diff --git a/lib/Components/segmented_enum_input_container.dart b/lib/Components/segmented_enum_input_container.dart index 00df92a..f40cecc 100644 --- a/lib/Components/segmented_enum_input_container.dart +++ b/lib/Components/segmented_enum_input_container.dart @@ -42,16 +42,30 @@ class _SegmentedEnumInputContainerState @override Widget build(BuildContext context) { - return FormField( - initialValue: selectedValue, - builder: (state) { - return InputDecorator( - decoration: InputDecoration( - labelText: widget.label, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(widget.borderRadius)), - contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 20), + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Align( + alignment: AlignmentDirectional.centerStart, + child: Text( + widget.label, + style: const TextStyle( + fontWeight: FontWeight.w400, + fontSize: 16, + ), ), + ), + const SizedBox(height: 8, width: 10), + Container( + width: 275, + height: 120, + padding: const EdgeInsets.all(4), + /*decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(widget.borderRadius), + border: Border.all(color: Colors.grey.shade400, width: 1.2), + ),*/ child: Row( children: widget.values.map((v) { bool isSelected = v == selectedValue; @@ -61,27 +75,28 @@ class _SegmentedEnumInputContainerState onTap: () { setState(() { selectedValue = v; - state.didChange(v); }); widget.onChanged(v); }, child: AnimatedContainer( - duration: Duration(milliseconds: 250), - margin: EdgeInsets.symmetric(horizontal: 4), - padding: EdgeInsets.symmetric(vertical: 10), + duration: const Duration(milliseconds: 250), + margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), + padding: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( - color: isSelected ? widget.selectedColor : kSecond, + color: isSelected ? widget.selectedColor : widget.unselectedColor, borderRadius: BorderRadius.circular(widget.borderRadius), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(data['icon'], color: widget.textColor), - SizedBox(width: 4), + const SizedBox(width: 4), Text( data['label'], style: TextStyle( - color: widget.textColor, fontWeight: FontWeight.w400), + color: widget.textColor, + fontWeight: FontWeight.w400, + ), ), ], ), @@ -90,8 +105,8 @@ class _SegmentedEnumInputContainerState ); }).toList(), ), - ); - }, + ), + ], ); } } diff --git a/lib/Components/single_choice_input_container.dart b/lib/Components/single_choice_input_container.dart index fbe1f5e..d02b95c 100644 --- a/lib/Components/single_choice_input_container.dart +++ b/lib/Components/single_choice_input_container.dart @@ -2,11 +2,12 @@ import 'package:flutter/material.dart'; import 'package:manager_api_new/api.dart'; import 'package:manager_app/constants.dart'; -class SingleChoiceInputContainer extends StatelessWidget { +class SingleChoiceInputContainer extends StatefulWidget { final String label; + final String selectLabel; final T? selected; final List values; - final ValueChanged onChanged; + final ValueChanged onChanged; final double borderRadius; final Color selectedColor; final Color textColor; @@ -14,6 +15,7 @@ class SingleChoiceInputContainer extends StatelessWidget { const SingleChoiceInputContainer({ Key? key, required this.label, + required this.selectLabel, required this.selected, required this.values, required this.onChanged, @@ -22,43 +24,82 @@ class SingleChoiceInputContainer extends StatelessWidget { this.textColor = kWhite, }) : super(key: key); + @override + State> createState() => + _SingleChoiceInputContainerState(); +} + +class _SingleChoiceInputContainerState + extends State> { + T? _selected; + + @override + void initState() { + super.initState(); + _selected = widget.selected; + } + @override Widget build(BuildContext context) { - return FormField( - initialValue: selected, + return FormField( + initialValue: _selected, builder: (state) { - return InputDecorator( - decoration: InputDecoration( - labelText: label, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(borderRadius), + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.label, + style: const TextStyle( + fontWeight: FontWeight.w400, + fontSize: 16, + ), ), - contentPadding: - const EdgeInsets.symmetric(horizontal: 25, vertical: 20), - ), - child: DropdownButtonHideUnderline( - child: DropdownButton( - value: selected, - isExpanded: true, - // todo handle view - items: values.map((v) { - return DropdownMenuItem( - value: v, - child: Row( - children: [ - Text((v as SectionEventDTO).label ?? ""), // TODO Update to handle more types ! - ], - ), - ); - }).toList(), - onChanged: (value) { - if (value != null) { - onChanged(value); - state.didChange(value); - } - }, + const SizedBox(height: 8, width: 10), + Container( + 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( + underline: const SizedBox( + child: Divider(height: 0, thickness: 1.5, color: kPrimaryColor), + ), + focusColor: Colors.transparent, + icon: const Icon(Icons.arrow_drop_down, color: kPrimaryColor), + isExpanded: true, + value: _selected, + hint: Text( + widget.selectLabel, + style: TextStyle(color: Colors.grey.shade600), + ), + items: widget.values.map((v) { + return DropdownMenuItem( + value: v, + child: Text( + (v as SectionEventDTO).label ?? "", + style: const TextStyle(fontSize: 14), + ), + ); + }).toList(), + onChanged: (value) { + setState(() { + if (value is SectionEventDTO && value.id == null) { + _selected = null; // affiche le hint + } else { + _selected = value; + } + }); + widget.onChanged(_selected); + state.didChange(_selected); + }, + ), ), - ), + ], ); }, ); diff --git a/lib/Screens/Applications/app_configuration_link_screen.dart b/lib/Screens/Applications/app_configuration_link_screen.dart index 0fae0be..f4c70b6 100644 --- a/lib/Screens/Applications/app_configuration_link_screen.dart +++ b/lib/Screens/Applications/app_configuration_link_screen.dart @@ -36,168 +36,244 @@ class _AppConfigurationLinkScreenState extends State ManagerAppContext managerAppContext = appContext.getContext() as ManagerAppContext; _generalInfoCard() { + + var elementWidth = 400.0; + var elementHeight = 125.0; + return Card( margin: const EdgeInsets.symmetric(vertical: 8), - color: kSecond, - elevation: 2, + color: kWhite, + elevation: 0, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Informations générales", style: Theme.of(context).textTheme.titleMedium), + Text("Informations générales", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)), SizedBox(height: 8), - Wrap( - spacing: 16, - runSpacing: 16, - /*GridView.count( - crossAxisCount: size.width > 800 ? 2 : 1, - crossAxisSpacing: 8, - mainAxisSpacing: 8, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - childAspectRatio: 3,*/ - children: [ - // Titre affiché main - /*SizedBox( - width: 300, - child: MultiStringInputContainer( - label: "Titre affiché:", - modalLabel: "Titre", + Expanded( + child: Center( + child: SingleChildScrollView( + child: Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + runAlignment: WrapAlignment.center, + spacing: 16, + runSpacing: 16, + children: [ + // Image principale + Container( + width: elementWidth, + height: elementHeight, + child: Center( + child: ResourceInputContainer( + label: "Image principale :", + initialValue: widget.applicationInstanceDTO.mainImageId, color: kPrimaryColor, - initialValue: [], - onGetResult: (value) { - /*if (sectionDTO.title! != value) { - sectionDTO.title = value; - save(true, appContext); - }*/ - }, - maxLines: 1, - isHTML: true, - isTitle: true, - ), - ),*/ - // Image principale - SizedBox( - width: 300, - child: ResourceInputContainer( - label: "Image principale :", - initialValue: widget.applicationInstanceDTO.mainImageId, - color: kPrimaryColor, - imageFit: BoxFit.fitHeight, - onChanged: (ResourceDTO resource) { - if(resource.id == null) { - widget.applicationInstanceDTO.mainImageId = null; - widget.applicationInstanceDTO.mainImageUrl = null; - } else { - widget.applicationInstanceDTO.mainImageId = resource.id; - widget.applicationInstanceDTO.mainImageUrl = resource.url; - } - }, - ), - ), - // Image Loader - SizedBox( - width: 300, - child: ResourceInputContainer( - label: "Loader :", - initialValue: widget.applicationInstanceDTO.loaderImageId, - color: kPrimaryColor, - imageFit: BoxFit.fitHeight, - onChanged: (ResourceDTO resource) { - if(resource.id == null) { - widget.applicationInstanceDTO.loaderImageId = null; - widget.applicationInstanceDTO.loaderImageUrl = null; - } else { - widget.applicationInstanceDTO.loaderImageId = resource.id; - widget.applicationInstanceDTO.loaderImageUrl = resource.url; - } - }, - ), - ), - // Primary color - SizedBox( - width: 300, - child: ColorPickerInputContainer( - label: "Couleur principale :", - fontSize: 20, - color: widget.applicationInstanceDTO.primaryColor, - onChanged: (value) { - widget.applicationInstanceDTO.primaryColor = value; - }, - ), - ), - // Secondary color - SizedBox( - width: 300, - child: ColorPickerInputContainer( - label: "Couleur secondaire :", - fontSize: 20, - color: widget.applicationInstanceDTO.secondaryColor, - onChanged: (value) { - widget.applicationInstanceDTO.secondaryColor = value; - }, - ), - ), - // Layout (Grid or Mansonry) - SizedBox( - width: 300, - child: SegmentedEnumInputContainer( - label: "Affichage :", - selected: LayoutMainPageType.MasonryGrid, - values: LayoutMainPageType.values, - inputValues: { LayoutMainPageType.SimpleGrid: {'label': 'Grille', 'icon': Icons.grid_view}, LayoutMainPageType.MasonryGrid : {'label': 'Masonry', 'icon': Icons.view_quilt }}, - onChanged: (value) { - var tempOutput = value; - widget.applicationInstanceDTO.layoutMainPage = tempOutput; - //print(configurationDTO.languages); - }, - ) - ), - // Langues - SizedBox( - width: 300, - child: MultiSelectDropdownLanguageContainer( - label: "Langues :", - initialValue: widget.applicationInstanceDTO.languages != null ? widget.applicationInstanceDTO.languages!: [], - values: languages, - isMultiple: true, - fontSize: 20, - isAtLeastOne: true, - onChanged: (value) { - var tempOutput = new List.from(value); - widget.applicationInstanceDTO.languages = tempOutput; - //print(configurationDTO.languages); - }, - ), - ), - // Highlight / Event principal - SizedBox( - width: 300, - child: FutureBuilder( - future: getSectionEvents(appContext, widget.applicationInstanceDTO), - builder: (context, snapshot) { - var rawList = snapshot.data; - var rawSubsections = jsonDecode(jsonEncode(snapshot.data)); - rawSubsections = rawSubsections?.map((json) => SectionEventDTO.fromJson(json)).toList(); - List? sectionEvents = rawSubsections?.whereType().toList(); + imageFit: BoxFit.fitHeight, + onChanged: (ResourceDTO resource) async { + if(resource.id == null) { + widget.applicationInstanceDTO.mainImageId = null; + widget.applicationInstanceDTO.mainImageUrl = null; + } else { + widget.applicationInstanceDTO.mainImageId = resource.id; + widget.applicationInstanceDTO.mainImageUrl = resource.url; + } - return SingleChoiceInputContainer( - label: "Evènement à l'affiche :", - selected: widget.applicationInstanceDTO.sectionEventDTO, - values: sectionEvents != null ? sectionEvents.toList() : [], - onChanged: (SectionEventDTO sectionEvent) { - print("Sélectionné: $sectionEvent"); - print(sectionEvent.label); - print(sectionEvent.id); - widget.applicationInstanceDTO.sectionEventId = sectionEvent.id; - }, - ); - } - ) + // automatic save + var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO); + if(applicationLink != null) { + showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null); + setState(() { + + }); + } + }, + ), + ), + ), + // Image Loader + Container( + width: elementWidth, + height: elementHeight, + child: Center( + child: ResourceInputContainer( + label: "Loader :", + initialValue: widget.applicationInstanceDTO.loaderImageId, + color: kPrimaryColor, + imageFit: BoxFit.fitHeight, + onChanged: (ResourceDTO resource) async { + if(resource.id == null) { + widget.applicationInstanceDTO.loaderImageId = null; + widget.applicationInstanceDTO.loaderImageUrl = null; + } else { + widget.applicationInstanceDTO.loaderImageId = resource.id; + widget.applicationInstanceDTO.loaderImageUrl = resource.url; + } + + // automatic save + var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO); + if(applicationLink != null) { + showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null); + setState(() { + + }); + } + }, + ), + ), + ), + // Primary color + Container( + width: elementWidth, + height: elementHeight, + child: Center( + child: ColorPickerInputContainer( + label: "Couleur principale :", + fontSize: 20, + color: widget.applicationInstanceDTO.primaryColor, + onChanged: (value) async { + widget.applicationInstanceDTO.primaryColor = value; + // automatic save + var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO); + if(applicationLink != null) { + showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null); + setState(() { + + }); + } + }, + ), + ), + ), + // Secondary color + SizedBox( + width: elementWidth, + height: elementHeight, + child: Center( + child: ColorPickerInputContainer( + label: "Couleur secondaire :", + fontSize: 20, + color: widget.applicationInstanceDTO.secondaryColor, + onChanged: (value) async { + widget.applicationInstanceDTO.secondaryColor = value; + // automatic save + var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO); + if(applicationLink != null) { + showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null); + setState(() { + + }); + } + }, + ), + ), + ), + // Layout (Grid or Mansonry) + SizedBox( + width: elementWidth, + height: elementHeight, + child: Center( + child: SegmentedEnumInputContainer( + label: "Affichage :", + selected: LayoutMainPageType.MasonryGrid, + 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; + // automatic save + var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO); + if(applicationLink != null) { + showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null); + setState(() { + + }); + } + //print(configurationDTO.languages); + }, + ), + ) + ), + // Langues + SizedBox( + width: elementWidth, + height: elementHeight, + child: Center( + child: MultiSelectDropdownLanguageContainer( + label: "Langues :", + initialValue: widget.applicationInstanceDTO.languages != null ? widget.applicationInstanceDTO.languages!: [], + values: languages, + isMultiple: true, + fontSize: 20, + isAtLeastOne: true, + onChanged: (value) async { + var tempOutput = new List.from(value); + widget.applicationInstanceDTO.languages = tempOutput; + // automatic save + var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO); + if(applicationLink != null) { + showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null); + setState(() { + + }); + } + //print(configurationDTO.languages); + }, + ), + ), + ), + // Highlight / Event principal + Container( + width: elementWidth, + height: elementHeight, + child: Center( + child: FutureBuilder( + future: getSectionEvents(appContext, widget.applicationInstanceDTO), + builder: (context, snapshot) { + var rawList = snapshot.data; + var rawSubsections = jsonDecode(jsonEncode(snapshot.data)); + rawSubsections = rawSubsections?.map((json) => SectionEventDTO.fromJson(json)).toList(); + List? sectionEvents = rawSubsections?.whereType().toList(); + sectionEvents = sectionEvents == null ? [] : sectionEvents; + + sectionEvents.add(SectionEventDTO(id: null, label: "Aucun")); + + return SingleChoiceInputContainer( + label: "Evènement à l'affiche :", + selectLabel: "Choisir un évènement", + selected: widget.applicationInstanceDTO.sectionEventDTO, + values: sectionEvents.toList(), + onChanged: (SectionEventDTO? sectionEvent) async { + if(sectionEvent == null) { + widget.applicationInstanceDTO.sectionEventId = null; + widget.applicationInstanceDTO.sectionEventDTO = null; + return; + } + print("Sélectionné: $sectionEvent"); + print(sectionEvent.label); + print(sectionEvent.id); + widget.applicationInstanceDTO.sectionEventId = sectionEvent.id; + + // automatic save + var applicationLink = await updateApplicationInstance(appContext, widget.applicationInstanceDTO); + if(applicationLink != null) { + showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null); + setState(() { + + }); + } + }, + ); + } + ), + ), + ), + ], + ), ), - ], - ) // tes champs + ), + ) ], ), ), @@ -207,206 +283,169 @@ class _AppConfigurationLinkScreenState extends State _phoneConfigCard(List? appConfigurationLinks) { return Card( margin: const EdgeInsets.symmetric(vertical: 8), - color: kSecond, - elevation: 2, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Configurations sur le téléphone", style: Theme.of(context).textTheme.titleMedium), - SizedBox(height: 8), - appConfigurationLinks != null ? SingleChildScrollView( - child: Stack( - children: [ - Container( - height: size.height * 0.6, - width: size.width * 0.8, - constraints: BoxConstraints( - minHeight: 300, - minWidth: 300, - maxHeight: 500 - ), - color: Colors.blue, - child: ReorderableCustomList( - items: appConfigurationLinks, - shrinkWrap: true, - onChanged: (updatedList) async { - int order = 0; - // update order manually - for(var item in updatedList) { - item.order = order; - order++; - } - // TODO use order put method - var result = await updateAppConfigurationOrder(appContext, updatedList); - setState(() { - // for refresh - }); - }, - 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); + color: kWhite, + elevation: 0, + child: Stack( + //crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: Text("Configurations sur le téléphone", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)), + ), + appConfigurationLinks != null ? Padding( + padding: const EdgeInsets.only(left: 32, right: 32, top: 75), + child: Container( + height: size.height * 0.6, + width: size.width * 0.8, + constraints: BoxConstraints( + minHeight: 300, + minWidth: 300, + maxHeight: 500 + ), + //color: Colors.blue, + child: SingleChildScrollView( + child: ReorderableCustomList( + items: appConfigurationLinks, + shrinkWrap: true, + onChanged: (updatedList) async { + int order = 0; + // update order manually + for(var item in updatedList) { + item.order = order; + order++; + } + // TODO use order put method + var result = await updateAppConfigurationOrder(appContext, updatedList); + setState(() { + // for refresh + 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); } - 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, - width: 70, - child: Switch( - activeThumbColor: kPrimaryColor, - inactiveThumbColor: kBodyTextColor, - inactiveTrackColor: kSecond, - hoverColor: kPrimaryColor.withValues(alpha: 0.2), - value: link.isActive ?? false, - onChanged: (bool newValue) async { - try { - link.isActive = newValue; - var applicationLink = await updateApplicationLink(appContext, link); - 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, + width: 70, + child: Switch( + activeThumbColor: kPrimaryColor, + inactiveThumbColor: kBodyTextColor, + inactiveTrackColor: kSecond, + hoverColor: kPrimaryColor.withValues(alpha: 0.2), + value: link.isActive ?? false, + onChanged: (bool newValue) async { + try { + link.isActive = newValue; + var applicationLink = await updateApplicationLink(appContext, link); + 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); } - }, - ), - ); - }, - (BuildContext context, int index, AppConfigurationLinkDTO link) { - return Container( - height: 50, - width: 50, - child: InkWell( - onTap: () async { - showConfirmationDialog( - "Êtes-vous sûr de vouloir retirer cette configuration de l'application ?", - () {}, - () async { - try { - var result = await deleteConfigurationToApp(appContext, link, widget.applicationInstanceDTO); + setState(() { + link.isActive = applicationLink.isActive; + }); + } + } catch (e) { + showNotification(kError, kWhite, "Une erreur est survenue", context, null); + } + }, + ), + ); + }, + (BuildContext context, int index, AppConfigurationLinkDTO link) { + return Container( + height: 50, + width: 50, + child: InkWell( + onTap: () async { + showConfirmationDialog( + "Êtes-vous sûr de vouloir retirer cette configuration de l'application ?", + () {}, + () async { + try { + var result = await deleteConfigurationToApp(appContext, link, widget.applicationInstanceDTO); - showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null); + showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null); - setState(() { - // for refresh ui - }); - } catch(e) { - showNotification(kError, kWhite, 'Une erreur est survenue lors du retrait de la configuration', context, null); - } - }, - context - ); - }, - child: Icon(Icons.delete, color: kError, size: 25), - ), - ); - }, - ], - padding: const EdgeInsets.all(8), - itemBuilder: (context, index, appConfigurationLink) { - return Container( - decoration: BoxDecoration( - - ), - margin: const EdgeInsets.symmetric(vertical: 3), - padding: const EdgeInsets.all(8), - child: Row( - children: [ - if(appConfigurationLink.configuration!.imageId != null) - Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: kSecond.withValues(alpha: 0.65), - borderRadius: BorderRadius.circular(8.0), - image: DecorationImage( - fit: BoxFit.cover, - image: NetworkImage(appConfigurationLink.configuration!.imageSource!) - ), - ) - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text(appConfigurationLink.configuration?.label ?? ""), - ), - ], - ), - ); - }, - ), - ), - Positioned( - bottom: 20, - right: 20, - child: InkWell( - onTap: () async { - // Show configuration selector to link with ! - var result = await showAddConfigurationLink(context, appContext, managerAppContext.instanceDTO!, appConfigurationLinks.map((acl) => acl.configurationId!).toList() ?? []); - if(result != null) { - for(var configurationId in result) { - AppConfigurationLinkDTO appConfigurationLinkDTO = AppConfigurationLinkDTO( - applicationInstanceId: widget.applicationInstanceDTO.id, - configurationId: configurationId, - isActive: true, - isDate: false, - isHour: false, - isSectionImageBackground: false, - layoutMainPage: LayoutMainPageType.SimpleGrid + setState(() { + // for refresh ui + }); + } catch(e) { + showNotification(kError, kWhite, 'Une erreur est survenue lors du retrait de la configuration', context, null); + } + }, + context ); - await addConfigurationToApp(appContext, appConfigurationLinkDTO, widget.applicationInstanceDTO); - } - setState(() { - // Refresh ui - }); - } - }, - child: Container( - height: 85, - width: 85, - decoration: BoxDecoration( - color: kSuccess, - borderRadius: BorderRadius.circular(8.0), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon(Icons.add, size: 25, color: kWhite), - ) + }, + child: Icon(Icons.delete, color: kError, size: 25), + ), + ); + }, + ], + padding: const EdgeInsets.all(8), + itemBuilder: (context, index, appConfigurationLink) { + return Container( + decoration: BoxDecoration( + ), - ), - ), - /*PhoneMockup( + margin: const EdgeInsets.symmetric(vertical: 3), + padding: const EdgeInsets.all(8), + child: Row( + children: [ + if(appConfigurationLink.configuration!.imageId != null) + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: kSecond.withValues(alpha: 0.65), + borderRadius: BorderRadius.circular(8.0), + image: DecorationImage( + fit: BoxFit.cover, + image: NetworkImage(appConfigurationLink.configuration!.imageSource!) + ), + ) + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(appConfigurationLink.configuration?.label ?? ""), + ), + ], + ), + ); + }, + ), + ), + ), + /*PhoneMockup( child: Center( child: GridView.builder( shrinkWrap: true, @@ -460,11 +499,47 @@ class _AppConfigurationLinkScreenState extends State ), ), ),*/ - ], + ): Center(child: Text("No data")), + appConfigurationLinks != null ? Positioned( + top: 8, + right: 8, + child: InkWell( + onTap: () async { + // Show configuration selector to link with ! + var result = await showAddConfigurationLink(context, appContext, managerAppContext.instanceDTO!, appConfigurationLinks.map((acl) => acl.configurationId!).toList() ?? []); + if(result != null) { + for(var configurationId in result) { + AppConfigurationLinkDTO appConfigurationLinkDTO = AppConfigurationLinkDTO( + applicationInstanceId: widget.applicationInstanceDTO.id, + configurationId: configurationId, + isActive: true, + isDate: false, + isHour: false, + isSectionImageBackground: false, + layoutMainPage: LayoutMainPageType.SimpleGrid + ); + await addConfigurationToApp(appContext, appConfigurationLinkDTO, widget.applicationInstanceDTO); + } + setState(() { + // Refresh ui + }); + } + }, + child: Container( + height: 60, + width: 60, + decoration: BoxDecoration( + color: kSuccess, + borderRadius: BorderRadius.circular(12.0), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.add, size: 24, color: kWhite), + ) ), - ): Center(child: Text("No data")), - ], - ), + ), + ) : SizedBox(), + ], ), ); } @@ -480,21 +555,35 @@ class _AppConfigurationLinkScreenState extends State final crossAxisCount = (screenWidth / itemWidth).floor().clamp(1, 6); - return Column( - children: [ - // autres widgets au-dessus si nécessaire - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - _generalInfoCard(), - _phoneConfigCard(appConfigurationLinks), - ], - ), + return LayoutBuilder( + builder: (context, constraints) { + final maxHeight = constraints.maxHeight; + + return SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: maxHeight * 0.45, + minHeight: 150, + ), + child: _generalInfoCard(), + ), + const SizedBox(height: 10), + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: maxHeight * 0.5, + minHeight: 150, + ), + child: _phoneConfigCard(appConfigurationLinks), + ), + ], ), - ), - ], + ); + }, ); return Align( diff --git a/lib/Screens/Main/main_screen.dart b/lib/Screens/Main/main_screen.dart index d6e2d0c..5dc499b 100644 --- a/lib/Screens/Main/main_screen.dart +++ b/lib/Screens/Main/main_screen.dart @@ -3,6 +3,7 @@ import 'dart:html'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Models/menu.dart'; @@ -70,105 +71,159 @@ class _MainScreenState extends State { Widget buildMenu(BuildContext context, AppContext appContext, ManagerAppContext managerAppContext, bool isDrawer) { return Container( width: isDrawer ? null : 250, // fixed width on sidebar, null on drawer for full width - color: kSecond, - child: Column( - children: [ - DrawerHeader( - child: Text( - menu.title, - style: TextStyle(color: kPrimaryColor, fontSize: 30, fontWeight: FontWeight.w400, fontFamily: "Helvetica"), - ), - ), - Expanded( - child: ListView( - padding: EdgeInsets.zero, - children: menu.sections!.map((section) { - final router = GoRouter.of(context); - final routeMatchList = router.routerDelegate.currentConfiguration; - var currentPath = routeMatchList.isNotEmpty ? routeMatchList.matches.first.matchedLocation : null; - - if (section.subMenu.isEmpty) { - return ListTile( - title: Text(section.name, style: TextStyle(color: currentPath!.contains(section.type) ? kPrimaryColor : kBodyTextColor, fontSize: 20)), - selected: currentPosition == section.menuId, - onTap: () { - //currentPosition.value = section.menuId; - context.go('/main/${section.type}'); - if (isDrawer) Navigator.of(context).pop(); // Close drawer on mobile - }, - ); - } else { - return ExpansionTile( - iconColor: currentPath!.contains("mobile") || currentPath.contains("kiosk") || currentPath.contains("web") || currentPath.contains("vr") ? kPrimaryColor : kBodyTextColor, - collapsedIconColor: currentPath.contains("mobile") || currentPath.contains("kiosk") || currentPath.contains("web") || currentPath.contains("vr") ? kPrimaryColor : kBodyTextColor, - title: Text(section.name, style: TextStyle(color: currentPath.contains("mobile") || currentPath.contains("kiosk") || currentPath.contains("web") || currentPath.contains("vr") ? kPrimaryColor : kBodyTextColor, fontSize: 20)), - children: section.subMenu.map((subSection) { - return ListTile( - title: Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - subSection.type == "mobile" ? Icon(Icons.phone_iphone, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) : - subSection.type == "kiosk" ? Icon(Icons.tablet_rounded, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) : - subSection.type == "web" ? Icon(Icons.public_outlined, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) : - subSection.type == "vr" ? Icon(Icons.panorama_photosphere, color: currentPath.contains(subSection.type)? kPrimaryColor : kBodyTextColor, size: 20) : SizedBox(), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text(subSection.name, style: TextStyle(color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, fontSize: 18)), - ) - ], - ), - ), - selected: currentPosition.value == subSection.menuId, - onTap: () { - - - if(currentPath != null && currentPath.contains(subSection.type)) { - // DO NOTHING, we are already display the correct interface - } else { - context.go('/main/${subSection.type}'); - } - if (isDrawer) Navigator.of(context).pop(); - }, - ); - }).toList(), - ); - } - }).toList(), - ), - ), - // Footer: Email + Logout button - Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - AutoSizeText( - (appContext.getContext() as ManagerAppContext).email ?? "", - style: TextStyle(color: kBodyTextColor, fontSize: 16, fontWeight: FontWeight.w300, fontFamily: "Helvetica"), - maxLines: 1, + child: Card( + color: kWhite, + margin: const EdgeInsets.symmetric(vertical: 8), + elevation: 0, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Container( + height: 150, + width: 250, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + constraints: BoxConstraints(maxHeight: 60, minHeight: 40), + child: SvgPicture.asset('assets/images/MyInfoMate_logo_only.svg') + ), + SizedBox(height: 16), + Text( + menu.title, + style: TextStyle( + color: kPrimaryColor, + fontSize: 25, + fontWeight: FontWeight.w400, + fontFamily: "Helvetica", + ), + ), + ], ), - IconButton( - icon: Icon(Icons.logout, color: kPrimaryColor), - onPressed: () async { - var session = await loadJsonSessionFile(); - setState(() { - Storage localStorage = window.localStorage; - localStorage.clear(); - ManagerAppContext managerAppContext = appContext.getContext(); - managerAppContext.accessToken = null; - managerAppContext.instanceId = null; - managerAppContext.instanceDTO = null; - appContext.setContext(managerAppContext); - - context.go('/login'); - }); - }, - ) - ], + ), ), - ), - ], + SizedBox(height: 25), + Expanded( + child: Theme( + data: Theme.of(context).copyWith(dividerColor: Colors.transparent), + child: ListView( + padding: EdgeInsets.zero, + children: menu.sections!.map((section) { + final router = GoRouter.of(context); + final routeMatchList = router.routerDelegate.currentConfiguration; + var currentPath = routeMatchList.isNotEmpty ? routeMatchList.matches.first.matchedLocation : null; + + if (section.subMenu.isEmpty) { + return Container( + decoration: currentPath!.contains(section.type) + ? BoxDecoration( + border: Border( + right: BorderSide( + color: kPrimaryColor, + width: 2, + ), + ), + ) + : null, + child: ListTile( + title: Text(section.name, style: TextStyle(color: currentPath.contains(section.type) ? kPrimaryColor : kBodyTextColor, fontSize: 22, fontWeight: currentPath.contains(section.type) ? FontWeight.w500 : FontWeight.w100)), + selected: currentPosition == section.menuId, + onTap: () { + //currentPosition.value = section.menuId; + context.go('/main/${section.type}'); + if (isDrawer) Navigator.of(context).pop(); // Close drawer on mobile + }, + ), + ); + } else { + return Container( + child: ExpansionTile( + iconColor: currentPath!.contains("mobile") || currentPath.contains("kiosk") || currentPath.contains("web") || currentPath.contains("vr") ? kPrimaryColor : kBodyTextColor, + collapsedIconColor: currentPath.contains("mobile") || currentPath.contains("kiosk") || currentPath.contains("web") || currentPath.contains("vr") ? kPrimaryColor : kBodyTextColor, + title: Text(section.name, style: TextStyle(color: currentPath.contains("mobile") || currentPath.contains("kiosk") || currentPath.contains("web") || currentPath.contains("vr") ? kPrimaryColor : kBodyTextColor, fontSize: 22, fontWeight: currentPath.contains("mobile") || currentPath.contains("kiosk") || currentPath.contains("web") || currentPath.contains("vr") ? FontWeight.w500 : FontWeight.w100)), + children: section.subMenu.map((subSection) { + return Container( + decoration: currentPath.contains(subSection.type) + ? BoxDecoration( + border: Border( + right: BorderSide( + color: kPrimaryColor, + width: 2, + ), + ), + ) : null, + child: ListTile( + title: Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + subSection.type == "mobile" ? Icon(Icons.phone_iphone, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) : + subSection.type == "kiosk" ? Icon(Icons.tablet_rounded, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) : + subSection.type == "web" ? Icon(Icons.public_outlined, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) : + subSection.type == "vr" ? Icon(Icons.panorama_photosphere, color: currentPath.contains(subSection.type)? kPrimaryColor : kBodyTextColor, size: 20) : SizedBox(), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(subSection.name, style: TextStyle(color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, fontSize: 18)), + ) + ], + ), + ), + selected: currentPosition.value == subSection.menuId, + onTap: () { + + + if(currentPath != null && currentPath.contains(subSection.type)) { + // DO NOTHING, we are already display the correct interface + } else { + context.go('/main/${subSection.type}'); + } + if (isDrawer) Navigator.of(context).pop(); + }, + ), + ); + }).toList(), + ), + ); + } + }).toList(), + ), + ), + ), + // Footer: Email + Logout button + Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + AutoSizeText( + (appContext.getContext() as ManagerAppContext).email ?? "", + style: TextStyle(color: kBodyTextColor, fontSize: 16, fontWeight: FontWeight.w300, fontFamily: "Helvetica"), + maxLines: 1, + ), + IconButton( + icon: Icon(Icons.logout, color: kPrimaryColor), + onPressed: () async { + var session = await loadJsonSessionFile(); + setState(() { + Storage localStorage = window.localStorage; + localStorage.clear(); + ManagerAppContext managerAppContext = appContext.getContext(); + managerAppContext.accessToken = null; + managerAppContext.instanceId = null; + managerAppContext.instanceDTO = null; + appContext.setContext(managerAppContext); + + context.go('/login'); + }); + }, + ) + ], + ), + ), + ], + ), ), ); } @@ -180,16 +235,16 @@ class _MainScreenState extends State { ManagerAppContext managerAppContext = appContext.getContext(); Size size = MediaQuery.of(context).size; - bool isMobile = size.width < 700; + bool isMobile = size.width < 850; return Scaffold( appBar: isMobile ? AppBar( - title: Text(menu.title, style: TextStyle(color: kPrimaryColor)), - backgroundColor: kSecond, + title: Text(menu.title, style: TextStyle(color: kWhite)), + backgroundColor: kPrimaryColor, leading: Builder( builder: (context) => IconButton( - icon: Icon(Icons.menu, color: kPrimaryColor), + icon: Icon(Icons.menu, color: kWhite), onPressed: () => Scaffold.of(context).openDrawer(), ), ), diff --git a/lib/Screens/login_screen.dart b/lib/Screens/login_screen.dart index abe2d1e..5766d90 100644 --- a/lib/Screens/login_screen.dart +++ b/lib/Screens/login_screen.dart @@ -3,6 +3,7 @@ import 'dart:html'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; import 'package:manager_app/Components/common_loader.dart'; import 'package:manager_app/Components/message_notification.dart'; @@ -267,7 +268,7 @@ class _LoginScreenState extends State { child: Container( height: size.height *0.7, width: size.width *0.4, - constraints: BoxConstraints(minWidth: 400, minHeight: 600), + constraints: BoxConstraints(minWidth: 400, minHeight: 500), decoration: BoxDecoration( color: kWhite, borderRadius: BorderRadius.circular(8.0), @@ -281,7 +282,7 @@ class _LoginScreenState extends State { ], ), child: Padding( - padding: const EdgeInsets.only(left: 60.0, right: 60.0), + padding: const EdgeInsets.only(left: 50.0, right: 50.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -290,9 +291,16 @@ class _LoginScreenState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Padding( + /*Padding( padding: const EdgeInsets.all(8.0), child: Icon(Icons.museum_outlined, color: kPrimaryColor, size: size.height*0.08), + ),*/ + Padding( + padding: const EdgeInsets.all(20.0), + child: Container( + constraints: BoxConstraints(maxHeight: 125, minHeight: 40), + child: SvgPicture.asset('assets/images/MyInfoMate_logo_only.svg') + ), ), Center( child: AutoSizeText( diff --git a/lib/constants.dart b/lib/constants.dart index 2953017..b7627dc 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -5,10 +5,10 @@ import 'package:manager_api_new/api.dart'; // Colors - TO FILL WIT CORRECT COLOR //const kBackgroundColor = Color(0xFFFFFFFF); const kTitleTextColor = Color(0xFF303030); -const kBodyTextColor = Color(0xFF4B4B4B); // TODO +const kBodyTextColor = Color(0xFF393939); // TODO const kBackgroundColor = Color(0xFFf5f5f7); -const kPrimaryColor = Color(0xFF308aae); +const kPrimaryColor = Color(0xFF264863); // #264863 // 308aae const kError = Color(0xFFCA413F); const kTextLightColor = Color(0xFFFCFDFD); const kSecond = Color(0xFFC2C9D6); diff --git a/pubspec.lock b/pubspec.lock index 6a1c37a..316973d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -479,13 +479,13 @@ packages: source: hosted version: "2.0.20" flutter_svg: - dependency: transitive + dependency: "direct main" description: name: flutter_svg - sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" + sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678 url: "https://pub.dev" source: hosted - version: "2.0.10+1" + version: "2.2.1" flutter_test: dependency: "direct dev" description: flutter @@ -1400,10 +1400,10 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.19" vector_graphics_codec: dependency: transitive description: @@ -1416,10 +1416,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" + sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.19" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d310352..be1b9ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: #filepicker_windows: ^2.0.0 file_picker: ^6.1.1 flare_flutter: ^3.0.1 + flutter_svg: ^2.2.1 #dart_vlc: ^0.0.6 #video_player: ^2.1.1 drag_and_drop_lists: ^0.3.2