Wip manager app, update to support web + fix section event layout

This commit is contained in:
Thomas Fransolet 2026-05-07 16:48:58 +02:00
parent 7669f38ed8
commit 2e8b82aa80
38 changed files with 2775 additions and 2558 deletions

View File

@ -15,12 +15,22 @@ import 'flag_decoration.dart';
showMultiStringInput (String label, String modalLabel, bool isTitle, List<TranslationDTO> values, List<TranslationDTO> newValues, Function onGetResult, int maxLines, List<ResourceType>? resourceTypes, BuildContext context) { /*Function onSelect,*/ showMultiStringInput (String label, String modalLabel, bool isTitle, List<TranslationDTO> values, List<TranslationDTO> newValues, Function onGetResult, int maxLines, List<ResourceType>? resourceTypes, BuildContext context) { /*Function onSelect,*/
showDialog( showDialog(
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
title: Center(child: Text(modalLabel)), child: SizedBox(
content: SingleChildScrollView( width: 560,
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(child: Text(modalLabel, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500))),
const SizedBox(height: 16),
Flexible(
child: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
Container( Container(
@ -45,7 +55,8 @@ showMultiStringInput (String label, String modalLabel, bool isTitle, List<Transl
], ],
) )
), ),
actions: <Widget>[ ),
const SizedBox(height: 16),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
@ -90,6 +101,9 @@ showMultiStringInput (String label, String modalLabel, bool isTitle, List<Transl
], ],
), ),
], ],
),
),
),
), context: context ), context: context
); );
} }

View File

@ -38,9 +38,7 @@ class _ResourceTabState extends State<ResourceTab> with SingleTickerProviderStat
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Column(
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
TabBar( TabBar(
@ -58,8 +56,6 @@ class _ResourceTabState extends State<ResourceTab> with SingleTickerProviderStat
), ),
), ),
], ],
),
),
); );
} }

View File

@ -1,4 +1,3 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:manager_app/Components/rounded_input_field.dart'; import 'package:manager_app/Components/rounded_input_field.dart';
import 'package:manager_app/constants.dart'; import 'package:manager_app/constants.dart';
@ -28,25 +27,18 @@ class StringInputContainer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; return Row(
return Container(
child: Row(
children: [ children: [
Align( Text(
alignment: AlignmentDirectional.centerStart,
child: Text(
label, label,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontSize: 16, fontSize: 16,
), ),
), ),
), Expanded(
Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Container(
width: isUrl ? size.width *0.6 : isSmall ? size.width *0.1 : size.width *0.25,
child: RoundedInputField( child: RoundedInputField(
color: color!, color: color!,
textColor: kBlack, textColor: kBlack,
@ -58,7 +50,6 @@ class StringInputContainer extends StatelessWidget {
), ),
), ),
], ],
),
); );
} }
} }

View File

@ -25,7 +25,7 @@ dynamic showAddConfigurationLink (BuildContext mainContext, AppContext appContex
future: getConfigurations(appContext), future: getConfigurations(appContext),
builder: (context, AsyncSnapshot<dynamic> snapshot) { builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
List<ConfigurationDTO> configurations = snapshot.data; List<ConfigurationDTO> configurations = snapshot.data ?? [];
// filter by already linked // filter by already linked
configurations = configurations.where((c) => !configurationIds.contains(c.id)).toList(); configurations = configurations.where((c) => !configurationIds.contains(c.id)).toList();
@ -61,7 +61,7 @@ dynamic showAddConfigurationLink (BuildContext mainContext, AppContext appContex
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
color: kWhite, color: kWhite,
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
image: configuration.imageId != null image: configuration.imageSource != null
? DecorationImage( ? DecorationImage(
fit: BoxFit.cover, fit: BoxFit.cover,
image: NetworkImage(configuration.imageSource!), image: NetworkImage(configuration.imageSource!),
@ -77,9 +77,9 @@ dynamic showAddConfigurationLink (BuildContext mainContext, AppContext appContex
), ),
), ),
), ),
title: Text(configuration.label!, style: Theme.of(context).textTheme.titleMedium), title: Text(configuration.label ?? '', style: Theme.of(context).textTheme.titleMedium),
subtitle: Text(configuration.dateCreation!.toString(), style: Theme.of(context).textTheme.bodyMedium), subtitle: Text(configuration.dateCreation?.toString() ?? '', style: Theme.of(context).textTheme.bodyMedium),
trailing: Text(configuration.languages!.toString(), style: Theme.of(context).textTheme.bodyMedium), trailing: Text(configuration.languages?.toString() ?? '', style: Theme.of(context).textTheme.bodyMedium),
onTap: () { onTap: () {
setState(() { setState(() {
if (isSelected) { if (isSelected) {

View File

@ -23,7 +23,10 @@ import 'package:provider/provider.dart';
class AppConfigurationLinkScreen extends StatefulWidget { class AppConfigurationLinkScreen extends StatefulWidget {
final ApplicationInstanceDTO applicationInstanceDTO; final ApplicationInstanceDTO applicationInstanceDTO;
AppConfigurationLinkScreen({Key? key, required this.applicationInstanceDTO}) : super(key: key); final bool showAssistant;
final String? configTitle;
final String? appUpdatedLabel;
AppConfigurationLinkScreen({Key? key, required this.applicationInstanceDTO, this.showAssistant = true, this.configTitle, this.appUpdatedLabel}) : super(key: key);
@override @override
_AppConfigurationLinkScreenState createState() => _AppConfigurationLinkScreenState(); _AppConfigurationLinkScreenState createState() => _AppConfigurationLinkScreenState();
@ -43,11 +46,11 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
final appContext = Provider.of<AppContext>(context); final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
ManagerAppContext managerAppContext = appContext.getContext() as ManagerAppContext; ManagerAppContext managerAppContext = appContext.getContext() as ManagerAppContext;
final appUpdatedMsg = widget.appUpdatedLabel ?? AppLocalizations.of(context)!.appUpdatedSuccess;
_generalInfoCard() { _generalInfoCard() {
var elementWidth = 400.0; const elementHeight = 125.0;
var elementHeight = 125.0;
return Card( return Card(
margin: const EdgeInsets.symmetric(vertical: 8), margin: const EdgeInsets.symmetric(vertical: 8),
@ -61,7 +64,10 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
Text(AppLocalizations.of(context)!.generalInfo, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)), Text(AppLocalizations.of(context)!.generalInfo, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)),
SizedBox(height: 8), SizedBox(height: 8),
Expanded( Expanded(
child: Center( child: LayoutBuilder(
builder: (context, constraints) {
final elementWidth = (constraints.maxWidth < 420) ? constraints.maxWidth : 400.0;
return Center(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Wrap( child: Wrap(
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
@ -92,7 +98,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// automatic save // automatic save
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO); var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) { if(applicationLink != null) {
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
/*setState(() { /*setState(() {
});*/ });*/
@ -123,7 +129,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// automatic save // automatic save
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO); var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) { if(applicationLink != null) {
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
/*setState(() { /*setState(() {
});*/ });*/
@ -146,7 +152,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// automatic save // automatic save
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO); var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) { if(applicationLink != null) {
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
/*setState(() { /*setState(() {
});*/ });*/
@ -169,7 +175,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// automatic save // automatic save
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO); var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) { if(applicationLink != null) {
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
/*setState(() { /*setState(() {
});*/ });*/
@ -194,7 +200,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// automatic save // automatic save
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO); var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) { if(applicationLink != null) {
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
/*setState(() { /*setState(() {
});*/ });*/
@ -222,7 +228,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// automatic save // automatic save
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO); var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) { if(applicationLink != null) {
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
/*setState(() { /*setState(() {
});*/ });*/
@ -274,7 +280,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// automatic save // automatic save
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO); var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
if(applicationLink != null) { if(applicationLink != null) {
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
//setState(() { //setState(() {
_applicationInstanceDTO.sectionEventDTO = applicationLink.sectionEventDTO; _applicationInstanceDTO.sectionEventDTO = applicationLink.sectionEventDTO;
//_applicationInstanceDTO = applicationLink; //_applicationInstanceDTO = applicationLink;
@ -287,7 +293,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
), ),
), ),
// Assistant IA visible uniquement si l'instance a la fonctionnalité // Assistant IA visible uniquement si l'instance a la fonctionnalité
if (managerAppContext.instanceDTO?.isAssistant == true) if (widget.showAssistant && managerAppContext.instanceDTO?.isAssistant == true)
Container( Container(
width: elementWidth, width: elementWidth,
height: elementHeight, height: elementHeight,
@ -312,7 +318,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
localSetState(() { localSetState(() {
_applicationInstanceDTO.isAssistant = applicationLink.isAssistant; _applicationInstanceDTO.isAssistant = applicationLink.isAssistant;
}); });
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
} }
} catch (e) { } catch (e) {
showNotification(kError, kWhite, AppLocalizations.of(context)!.errorOccurred, context, null); showNotification(kError, kWhite, AppLocalizations.of(context)!.errorOccurred, context, null);
@ -328,7 +334,9 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
], ],
), ),
), ),
), );
}
)
) )
], ],
), ),
@ -348,7 +356,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Text(AppLocalizations.of(context)!.phoneConfigTitle, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)), child: Text(widget.configTitle ?? AppLocalizations.of(context)!.phoneConfigTitle, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)),
), ),
appConfigurationLinks != null ? Padding( appConfigurationLinks != null ? Padding(
padding: const EdgeInsets.only(left: 32, right: 32, top: 75), padding: const EdgeInsets.only(left: 32, right: 32, top: 75),
@ -376,7 +384,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
// TODO use order put method // TODO use order put method
var result = await updateAppConfigurationOrder(appContext, updatedList); var result = await updateAppConfigurationOrder(appContext, updatedList);
localSetState(() {}); localSetState(() {});
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null); showNotification(kSuccess, kWhite, appUpdatedMsg, context, null);
}, },
actions: [ actions: [
(BuildContext context, int index, AppConfigurationLinkDTO link) { (BuildContext context, int index, AppConfigurationLinkDTO link) {

View File

@ -0,0 +1,194 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:manager_app/Screens/Applications/app_configuration_link_screen.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_api_new/api.dart';
import 'package:provider/provider.dart';
class WebInstanceLoader extends StatefulWidget {
const WebInstanceLoader({Key? key}) : super(key: key);
@override
State<WebInstanceLoader> createState() => _WebInstanceLoaderState();
}
class _WebInstanceLoaderState extends State<WebInstanceLoader> {
bool _creating = false;
String? _error;
ApplicationInstanceDTO? _instance;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _init());
}
void _init() {
final appContext = Provider.of<AppContext>(context, listen: false);
final managerCtx = appContext.getContext() as ManagerAppContext;
final existing = managerCtx.instanceDTO?.applicationInstanceDTOs
?.firstWhereOrNull((ai) => ai.appType == AppType.Web);
if (existing != null) {
setState(() => _instance = existing);
}
}
Future<void> _create() async {
final appContext = Provider.of<AppContext>(context, listen: false);
final managerCtx = appContext.getContext() as ManagerAppContext;
final instanceDTO = managerCtx.instanceDTO!;
setState(() {
_creating = true;
_error = null;
});
try {
final newInstance = ApplicationInstanceDTO(
instanceId: instanceDTO.id,
appType: AppType.Web,
languages: ['fr'],
layoutMainPage: LayoutMainPageType.MasonryGrid,
isAssistant: false,
isStatistic: false,
);
final created = await managerCtx.clientAPI!.applicationInstanceApi!
.applicationInstanceCreate(newInstance);
if (created != null) {
instanceDTO.applicationInstanceDTOs = [...?instanceDTO.applicationInstanceDTOs, created];
setState(() => _instance = created);
} else {
setState(() => _error = 'La création a échoué (réponse vide)');
}
} catch (e) {
setState(() => _error = e.toString());
} finally {
setState(() => _creating = false);
}
}
@override
Widget build(BuildContext context) {
if (_instance != null) {
return WebAppScreen(applicationInstanceDTO: _instance!);
}
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'L\'application web n\'est pas encore initialisée.',
style: TextStyle(color: kBodyTextColor),
),
if (_error != null) ...[
const SizedBox(height: 8),
Text(_error!, style: TextStyle(color: kError, fontSize: 12)),
],
const SizedBox(height: 16),
_creating
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _create,
child: const Text('Initialiser l\'application web'),
),
],
),
);
}
}
class WebAppScreen extends StatelessWidget {
final ApplicationInstanceDTO applicationInstanceDTO;
const WebAppScreen({Key? key, required this.applicationInstanceDTO}) : super(key: key);
@override
Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context);
final managerAppContext = appContext.getContext() as ManagerAppContext;
final instanceDTO = managerAppContext.instanceDTO;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (instanceDTO?.webSlug != null || instanceDTO?.publicApiKey != null)
Card(
margin: const EdgeInsets.fromLTRB(0, 0, 0, 8),
color: kWhite,
elevation: 0,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Accès web', style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)),
const SizedBox(height: 12),
_InfoRow(
label: 'URL visiteurs',
value: 'app.myinfomate.be/${instanceDTO?.webSlug ?? ''}',
),
const SizedBox(height: 8),
_InfoRow(
label: 'Clé publique API',
value: instanceDTO?.publicApiKey ?? '',
),
],
),
),
),
Expanded(
child: AppConfigurationLinkScreen(
applicationInstanceDTO: applicationInstanceDTO,
showAssistant: false,
configTitle: 'Contenu de l\'application web',
appUpdatedLabel: 'Application web mise à jour',
),
),
],
);
}
}
class _InfoRow extends StatelessWidget {
final String label;
final String value;
const _InfoRow({required this.label, required this.value});
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: TextStyle(fontSize: 12, color: kBodyTextColor)),
const SizedBox(height: 2),
Text(
value,
style: TextStyle(fontSize: 13, fontFamily: 'monospace', color: kPrimaryColor),
overflow: TextOverflow.ellipsis,
),
],
),
),
IconButton(
icon: Icon(Icons.copy_outlined, size: 18, color: kPrimaryColor),
tooltip: 'Copier',
onPressed: () {
Clipboard.setData(ClipboardData(text: value));
showNotification(kSuccess, kWhite, 'Copié !', context, null);
},
),
],
);
}
}

View File

@ -431,13 +431,14 @@ class _EventConfigState extends State<EventConfig> {
Divider(), Divider(),
// --- Parcours --- // --- Parcours ---
if (eventDTO.id != null) if (eventDTO.id != null)
ParcoursConfig( SizedBox(
height: 500,
child: ParcoursConfig(
initialValue: const [], initialValue: const [],
parentId: eventDTO.id!, parentId: eventDTO.id!,
isEvent: true, isEvent: true,
onChanged: (paths) { onChanged: (paths) {},
// parcours are managed independently, no DTO update needed here ),
},
), ),
], ],
); );

View File

@ -25,12 +25,19 @@ void showEditSubSection(SectionDTO subSectionDTO, Function getResult, AppContext
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
showDialog( showDialog(
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
content: Container( child: SizedBox(
width: size.width *0.85, width: MediaQuery.of(context).size.width * 0.85,
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
@ -139,7 +146,7 @@ void showEditSubSection(SectionDTO subSectionDTO, Function getResult, AppContext
), ),
), ),
), ),
actions: <Widget>[ const SizedBox(height: 16),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@ -180,6 +187,9 @@ void showEditSubSection(SectionDTO subSectionDTO, Function getResult, AppContext
], ],
), ),
], ],
),
),
),
), context: context ), context: context
); );
} }

View File

@ -45,12 +45,19 @@ Future<OrderedTranslationAndResourceDTO?> showNewOrUpdatePDFFile(OrderedTranslat
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
var result = await showDialog( var result = await showDialog(
builder: (BuildContext dialogContext) => AlertDialog( builder: (BuildContext dialogContext) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
content: Container( child: SizedBox(
width: size.width *0.5, width: 560,
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -105,7 +112,7 @@ Future<OrderedTranslationAndResourceDTO?> showNewOrUpdatePDFFile(OrderedTranslat
), ),
), ),
), ),
actions: <Widget>[ const SizedBox(height: 16),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@ -148,6 +155,9 @@ Future<OrderedTranslationAndResourceDTO?> showNewOrUpdatePDFFile(OrderedTranslat
], ],
), ),
], ],
),
),
),
), context: context ), context: context
); );
return result; return result;

View File

@ -34,12 +34,19 @@ Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO,
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
var result = await showDialog( var result = await showDialog(
builder: (BuildContext dialogContext) => AlertDialog( builder: (BuildContext dialogContext) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
content: Container( child: SizedBox(
width: size.width *0.5, width: 560,
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -124,7 +131,7 @@ Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO,
), ),
), ),
), ),
actions: <Widget>[ const SizedBox(height: 16),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@ -168,6 +175,9 @@ Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO,
], ],
), ),
], ],
),
),
),
), context: context ), context: context
); );
return result; return result;

View File

@ -34,13 +34,19 @@ Future<ContentDTO?> showNewOrUpdateContentSlider(ContentDTO? inputContentDTO, Ap
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
var result = await showDialog( var result = await showDialog(
builder: (BuildContext dialogContext) => AlertDialog( builder: (BuildContext dialogContext) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
content: Container( child: SizedBox(
width: size.width *0.35, width: 480,
constraints: BoxConstraints(minWidth: 300), child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -115,7 +121,7 @@ Future<ContentDTO?> showNewOrUpdateContentSlider(ContentDTO? inputContentDTO, Ap
), ),
), ),
), ),
actions: <Widget>[ const SizedBox(height: 16),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@ -157,6 +163,9 @@ Future<ContentDTO?> showNewOrUpdateContentSlider(ContentDTO? inputContentDTO, Ap
], ],
), ),
], ],
),
),
),
), context: context ), context: context
); );

View File

@ -68,6 +68,7 @@ class _SliderConfigState extends State<SliderConfig> {
children: [ children: [
Container( Container(
width: size.width * 0.95, width: size.width * 0.95,
height: 300,
child: ReorderableListView( child: ReorderableListView(
onReorder: _onReorder, onReorder: _onReorder,
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,

View File

@ -13,7 +13,6 @@ import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/l10n/app_localizations.dart'; import 'package:manager_app/l10n/app_localizations.dart';
import 'package:manager_app/Components/multi_string_input_container.dart'; import 'package:manager_app/Components/multi_string_input_container.dart';
import 'package:manager_app/Components/number_input_container.dart'; import 'package:manager_app/Components/number_input_container.dart';
import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Components/string_input_container.dart'; import 'package:manager_app/Components/string_input_container.dart';
import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/Screens/Configurations/Section/SubSection/Video/video_config.dart'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Video/video_config.dart';
@ -82,239 +81,186 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context); final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size;
Object? rawSectionData;
return FutureBuilder( return FutureBuilder(
future: _sectionFuture, future: _sectionFuture,
builder: (context, AsyncSnapshot<dynamic> snapshot) { builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
rawSectionData = snapshot.data; Object? rawSectionData = snapshot.data;
var nullableSection = SectionDTO.fromJson(rawSectionData); var nullableSection = SectionDTO.fromJson(rawSectionData);
if (nullableSection != null) { if (nullableSection != null) {
sectionDTO = nullableSection; sectionDTO = nullableSection;
// Only initialize sectionDetailDTO if it's not already loaded for this section if (sectionDetailDTO == null || lastLoadedSectionId != widget.id) {
if (sectionDetailDTO == null ||
lastLoadedSectionId != widget.id) {
_initializeSectionDetail(rawSectionData); _initializeSectionDetail(rawSectionData);
lastLoadedSectionId = widget.id; lastLoadedSectionId = widget.id;
} }
return Stack( return _buildBody(rawSectionData, appContext);
fit: StackFit.expand,
children: [
bodySection(
rawSectionData, size, appContext, context),
Align(
alignment: AlignmentDirectional.bottomCenter,
child: Container(
height: 80,
child: getButtons(sectionDTO, appContext),
),
)
],
);
} else { } else {
return Center( return Center(child: Text(AppLocalizations.of(context)!.sectionLoadError));
child: Text(
AppLocalizations.of(context)!.sectionLoadError));
} }
} else if (snapshot.connectionState == ConnectionState.none) { } else if (snapshot.connectionState == ConnectionState.none) {
return Text(AppLocalizations.of(context)!.noData); return Center(child: Text(AppLocalizations.of(context)!.noData));
} else {
return Center(
child: Container(
height: size.height * 0.2, child: CommonLoader()));
} }
}); return const Center(child: CommonLoader());
},
);
} }
Widget bodySection(Object? rawSectionDTO, Size size, AppContext appContext, // Main layout
BuildContext context) {
ManagerAppContext managerAppContext = appContext.getContext();
//SectionDTO? sectionDTO = SectionDTO.fromJson(rawSectionDTO);
return SingleChildScrollView( Widget _buildBody(Object? rawSectionData, AppContext appContext) {
final managerCtx = appContext.getContext() as ManagerAppContext;
final canEdit = managerCtx.canEdit;
final l = AppLocalizations.of(context)!;
return Column(
children: [
_buildHeader(appContext, l),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column( child: Column(
//mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Container( _cardIdentity(appContext, l),
//color: Colors.orangeAccent, const SizedBox(height: 12),
height: 82, _cardQR(appContext, l),
const SizedBox(height: 12),
_cardTypeConfig(rawSectionData, appContext),
const SizedBox(height: 80),
],
),
),
),
_buildFooter(appContext, canEdit, l),
],
);
}
// Header
Widget _buildHeader(AppContext appContext, AppLocalizations l) {
final typeName = sectionDTO.type?.toString().split('.').last ?? '';
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: kWhite,
border: Border(bottom: BorderSide(color: kSecond, width: 1)),
),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Align( IconButton(
alignment: AlignmentDirectional.bottomStart, icon: const Icon(Icons.arrow_back, color: kPrimaryColor),
child: Padding( onPressed: () {
padding: const EdgeInsets.all(3.0), ManagerAppContext ctx = appContext.getContext();
ctx.selectedSection = null;
appContext.setContext(ctx);
},
),
const SizedBox(width: 8),
Icon(getSectionIcon(sectionDTO.type), color: kPrimaryColor, size: 22),
const SizedBox(width: 8),
Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Text(
crossAxisAlignment: CrossAxisAlignment.center, sectionDTO.label ?? '',
children: [ style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
Padding( overflow: TextOverflow.ellipsis,
padding: const EdgeInsets.all(8.0), ),
child: Icon( Text(
getSectionIcon(sectionDTO.type), DateFormat('dd/MM/yyyy').format(sectionDTO.dateCreation!),
color: kPrimaryColor, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w300, color: Colors.grey),
size: 25, ),
],
), ),
), ),
Text(sectionDTO.label!, Chip(
style: TextStyle( label: Text(typeName.toUpperCase()),
fontSize: 30, backgroundColor: kPrimaryColor.withValues(alpha: 0.1),
fontWeight: FontWeight.w400)), labelStyle: const TextStyle(color: kPrimaryColor, fontWeight: FontWeight.w600, fontSize: 11),
//if((appContext.getContext() as ManagerAppContext).selectedConfiguration!.isMobile!) side: BorderSide.none,
visualDensity: VisualDensity.compact,
),
const SizedBox(width: 8),
DownloadPDF(sections: [sectionDTO]), 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)),
),
],
),
)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Align(
alignment: AlignmentDirectional.centerEnd,
child: InkWell(
onTap: () {
ManagerAppContext managerAppContext =
appContext.getContext();
managerAppContext.selectedSection = null;
appContext.setContext(managerAppContext);
},
child: Container(
child: Icon(
Icons.arrow_back,
color: kPrimaryColor,
size: 50.0,
))),
),
)
],
),
), // TITLE
Container(
//color: Colors.blue,
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
//if((appContext.getContext() as ManagerAppContext).selectedConfiguration!.isMobile!)
Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
onTap: () async {
var image = await _captureAndSharePng(
globalKey, sectionDTO.id!);
await readAndWriteFiles(image);
showNotification(
kSuccess,
kWhite,
AppLocalizations.of(context)!.qrCodeCopied,
context,
null);
},
child: Container(
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!}",
version: QrVersions.auto,
size: 50.0,
),
),
),
),
SelectableText(sectionDTO.id!,
style: new TextStyle(fontSize: 15))
],
),
CheckInputContainer(
label: AppLocalizations.of(context)!.beaconLabel,
isChecked: sectionDTO.isBeacon!,
onChanged: (value) {
setState(() {
sectionDTO.isBeacon = value;
save(false, appContext);
});
},
),
if (sectionDTO.isBeacon!)
NumberInputContainer(
label: AppLocalizations.of(context)!.beaconIdLabel,
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,
AppLocalizations.of(context)!.beaconMustBeNumber,
context,
null);
} }
},
// Footer
Widget _buildFooter(AppContext appContext, bool canEdit, AppLocalizations l) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: const BoxDecoration(
color: kWhite,
boxShadow: [BoxShadow(color: kSecond, blurRadius: 8, offset: Offset(0, -2))],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
OutlinedButton.icon(
icon: const Icon(Icons.undo, size: 16),
label: Text(l.cancel),
onPressed: () => cancel(appContext),
),
if (canEdit) ...[
const SizedBox(width: 8),
ElevatedButton.icon(
icon: const Icon(Icons.delete, size: 16, color: kWhite),
label: Text(l.delete, style: const TextStyle(color: kWhite)),
style: ElevatedButton.styleFrom(backgroundColor: kError),
onPressed: () => delete(appContext),
),
const SizedBox(width: 8),
ElevatedButton.icon(
icon: const Icon(Icons.done, size: 16, color: kWhite),
label: Text(l.save, style: const TextStyle(color: kWhite)),
style: ElevatedButton.styleFrom(backgroundColor: kPrimaryColor),
onPressed: () => save(false, appContext),
), ),
], ],
],
), ),
Column( );
mainAxisAlignment: MainAxisAlignment.spaceAround, }
// Card Identité
Widget _cardIdentity(AppContext appContext, AppLocalizations l) {
return Card(
elevation: 0,
color: kWhite,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( Text(l.identifierLabel.replaceAll(':', '').trim(),
height: 100, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: kTitleTextColor)),
child: StringInputContainer( const SizedBox(height: 12),
label: AppLocalizations.of(context)!.identifierLabel, LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth > kBreakpointMobile;
final fields = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StringInputContainer(
label: l.identifierLabel,
initialValue: sectionDTO.label, initialValue: sectionDTO.label,
onChanged: (String value) { onChanged: (String value) => sectionDTO.label = value,
sectionDTO.label = value;
},
), ),
), const SizedBox(height: 12),
SizedBox( MultiStringInputContainer(
height: 100, label: l.displayTitleLabel,
child: MultiStringInputContainer( modalLabel: l.messageTitle,
label: AppLocalizations.of(context)!.displayTitleLabel,
modalLabel: AppLocalizations.of(context)!.messageTitle,
color: kPrimaryColor, color: kPrimaryColor,
initialValue: sectionDTO.title!, initialValue: sectionDTO.title!,
onGetResult: (value) { onGetResult: (value) {
@ -327,32 +273,11 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
isHTML: true, isHTML: true,
isTitle: true, isTitle: true,
), ),
),
/*if(!(appContext.getContext() as ManagerAppContext).selectedConfiguration!.isMobile!)
MultiStringInputContainer(
label: "Description affichée:",
modalLabel: "Description",
color: kPrimaryColor,
isHTML: true,
initialValue: sectionDTO.description!,
isMandatory: false,
onGetResult: (value) {
if (sectionDTO.description != value) {
sectionDTO.description = value!;
save(true, appContext);
}
},
maxLines: 2,
isTitle: true,
),*/
], ],
), );
Column(
mainAxisAlignment: MainAxisAlignment.start, final image = ResourceInputContainer(
crossAxisAlignment: CrossAxisAlignment.start, label: l.imageLabel,
children: [
ResourceInputContainer(
label: AppLocalizations.of(context)!.imageLabel,
initialValue: sectionDTO.imageId, initialValue: sectionDTO.imageId,
color: kPrimaryColor, color: kPrimaryColor,
onChanged: (ResourceDTO resource) { onChanged: (ResourceDTO resource) {
@ -364,84 +289,143 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
sectionDTO.imageSource = resource.url; sectionDTO.imageSource = resource.url;
} }
}, },
), );
if (isWide) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(flex: 3, child: fields),
const SizedBox(width: 16),
Expanded(flex: 2, child: image),
], ],
) );
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fields,
const SizedBox(height: 12),
image,
], ],
), );
},
), ),
], ],
), ),
), ),
),
), // FIELDS SECTION
Container(
//width: size.width * 0.8,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: getSpecificData(rawSectionDTO, appContext),
),
decoration: BoxDecoration(
//color: Colors.lightGreen,
borderRadius: BorderRadius.circular(30),
border: Border.all(width: 1.5, color: kSecond)),
),
],
),
); );
} }
getButtons(SectionDTO sectionDTO, AppContext appContext) { // Card QR / Beacon
final canEdit = (appContext.getContext() as ManagerAppContext).canEdit;
return Align( Widget _cardQR(AppContext appContext, AppLocalizations l) {
alignment: AlignmentDirectional.bottomCenter, final managerCtx = appContext.getContext() as ManagerAppContext;
child: Row( final qrData = "${managerCtx.host!.replaceFirst("api", "web")}/${managerCtx.instanceId}/${managerCtx.selectedConfiguration!.id}/${sectionDTO.id!}";
mainAxisAlignment: MainAxisAlignment.end,
return Card(
elevation: 0,
color: kWhite,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Theme(
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
leading: const Icon(Icons.qr_code_2, color: kPrimaryColor),
title: const Text("QR Code / Identifiant",
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: kTitleTextColor)),
childrenPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
children: [ children: [
Padding( Wrap(
padding: const EdgeInsets.all(10.0), spacing: 24,
child: RoundedButton( runSpacing: 12,
text: AppLocalizations.of(context)!.cancel, crossAxisAlignment: WrapCrossAlignment.center,
icon: Icons.undo, children: [
color: Colors.grey, InkWell(
textColor: Colors.white, onTap: () async {
fontSize: 15, var image = await _captureAndSharePng(globalKey, sectionDTO.id!);
press: () { await readAndWriteFiles(image);
cancel(appContext); if (context.mounted) {
showNotification(kSuccess, kWhite, l.qrCodeCopied, context, null);
}
}, },
child: SizedBox(
width: 120,
height: 120,
child: RepaintBoundary(
key: globalKey,
child: QrImageView(
padding: const EdgeInsets.all(5.0),
data: qrData,
version: QrVersions.auto,
size: 50.0,
), ),
), ),
if (canEdit) Padding(
padding: const EdgeInsets.all(8.0),
child: RoundedButton(
text: AppLocalizations.of(context)!.delete,
icon: Icons.delete,
color: kError,
textColor: Colors.white,
fontSize: 15,
press: () {
delete(appContext);
},
), ),
), ),
if (canEdit) Padding( SelectableText(sectionDTO.id!, style: const TextStyle(fontSize: 13)),
padding: const EdgeInsets.all(8.0), ],
child: RoundedButton( ),
text: AppLocalizations.of(context)!.save, const SizedBox(height: 12),
icon: Icons.done, Wrap(
color: kSuccess, spacing: 16,
textColor: Colors.white, runSpacing: 8,
fontSize: 15, children: [
press: () { CheckInputContainer(
label: l.beaconLabel,
isChecked: sectionDTO.isBeacon!,
onChanged: (value) {
setState(() {
sectionDTO.isBeacon = value;
save(false, appContext); save(false, appContext);
});
}, },
), ),
if (sectionDTO.isBeacon!)
NumberInputContainer(
label: l.beaconIdLabel,
initialValue: sectionDTO.beaconId ?? 0,
isSmall: true,
onChanged: (value) {
try {
sectionDTO.beaconId = int.parse(value);
} catch (e) {
showNotification(Colors.orange, kWhite, l.beaconMustBeNumber, context, null);
}
},
), ),
], ],
), ),
],
),
),
); );
} }
// Card Type Config
Widget _cardTypeConfig(Object? rawSectionData, AppContext appContext) {
final typeName = sectionDTO.type?.toString().split('.').last ?? 'Section';
return Card(
elevation: 0,
color: kWhite,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Configuration $typeName",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: kTitleTextColor)),
const SizedBox(height: 12),
getSpecificData(rawSectionData, appContext),
],
),
),
);
}
// Actions
Future<void> cancel(AppContext appContext) async { Future<void> cancel(AppContext appContext) async {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
Object? rawData = await (appContext.getContext() as ManagerAppContext) Object? rawData = await (appContext.getContext() as ManagerAppContext)
@ -480,18 +464,16 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
if (isTraduction) { if (isTraduction) {
showNotification( showNotification(kSuccess, kWhite,
kSuccess, AppLocalizations.of(context)!.sectionTranslationSaved, context, null);
kWhite,
AppLocalizations.of(context)!.sectionTranslationSaved,
context,
null);
} else { } else {
showNotification(kSuccess, kWhite, showNotification(kSuccess, kWhite,
AppLocalizations.of(context)!.sectionSavedSuccess, context, null); AppLocalizations.of(context)!.sectionSavedSuccess, context, null);
} }
} }
// Type-specific config widget
getSpecificData(Object? rawSectionData, AppContext appContext) { getSpecificData(Object? rawSectionData, AppContext appContext) {
switch (sectionDTO.type) { switch (sectionDTO.type) {
case SectionType.Map: case SectionType.Map:
@ -527,9 +509,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
case SectionType.Menu: case SectionType.Menu:
return MenuConfig( return MenuConfig(
initialValue: sectionDetailDTO as MenuDTO, initialValue: sectionDetailDTO as MenuDTO,
onChanged: (String data) { onChanged: (String data) {},
//sectionDTO.data = data;
},
); );
case SectionType.Quiz: case SectionType.Quiz:
return QuizzConfig( return QuizzConfig(
@ -584,6 +564,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
} }
} }
// Section detail initialization
_initializeSectionDetail(Object? rawSectionData) { _initializeSectionDetail(Object? rawSectionData) {
if (rawSectionData == null) return; if (rawSectionData == null) return;
switch (sectionDTO.type) { switch (sectionDTO.type) {
@ -626,6 +608,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
} }
} }
// Sync sectionDTO fields back to detail DTO before save
updateSectionDetail() { updateSectionDetail() {
switch (sectionDTO.type) { switch (sectionDTO.type) {
case SectionType.Map: case SectionType.Map:
@ -634,8 +618,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as MapDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as MapDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as MapDTO).type = sectionDTO.type; (sectionDetailDTO as MapDTO).type = sectionDTO.type;
(sectionDetailDTO as MapDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as MapDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as MapDTO).configurationId = (sectionDetailDTO as MapDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as MapDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as MapDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as MapDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as MapDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as MapDTO).label = sectionDTO.label; (sectionDetailDTO as MapDTO).label = sectionDTO.label;
@ -655,8 +638,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as SliderDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as SliderDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as SliderDTO).type = sectionDTO.type; (sectionDetailDTO as SliderDTO).type = sectionDTO.type;
(sectionDetailDTO as SliderDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as SliderDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as SliderDTO).configurationId = (sectionDetailDTO as SliderDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as SliderDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as SliderDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as SliderDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as SliderDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as SliderDTO).label = sectionDTO.label; (sectionDetailDTO as SliderDTO).label = sectionDTO.label;
@ -676,8 +658,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as VideoDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as VideoDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as VideoDTO).type = sectionDTO.type; (sectionDetailDTO as VideoDTO).type = sectionDTO.type;
(sectionDetailDTO as VideoDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as VideoDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as VideoDTO).configurationId = (sectionDetailDTO as VideoDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as VideoDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as VideoDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as VideoDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as VideoDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as VideoDTO).label = sectionDTO.label; (sectionDetailDTO as VideoDTO).label = sectionDTO.label;
@ -697,8 +678,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as WebDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as WebDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as WebDTO).type = sectionDTO.type; (sectionDetailDTO as WebDTO).type = sectionDTO.type;
(sectionDetailDTO as WebDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as WebDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as WebDTO).configurationId = (sectionDetailDTO as WebDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as WebDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as WebDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as WebDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as WebDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as WebDTO).label = sectionDTO.label; (sectionDetailDTO as WebDTO).label = sectionDTO.label;
@ -718,8 +698,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as MenuDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as MenuDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as MenuDTO).type = sectionDTO.type; (sectionDetailDTO as MenuDTO).type = sectionDTO.type;
(sectionDetailDTO as MenuDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as MenuDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as MenuDTO).configurationId = (sectionDetailDTO as MenuDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as MenuDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as MenuDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as MenuDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as MenuDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as MenuDTO).label = sectionDTO.label; (sectionDetailDTO as MenuDTO).label = sectionDTO.label;
@ -739,8 +718,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as QuizDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as QuizDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as QuizDTO).type = sectionDTO.type; (sectionDetailDTO as QuizDTO).type = sectionDTO.type;
(sectionDetailDTO as QuizDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as QuizDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as QuizDTO).configurationId = (sectionDetailDTO as QuizDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as QuizDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as QuizDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as QuizDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as QuizDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as QuizDTO).label = sectionDTO.label; (sectionDetailDTO as QuizDTO).label = sectionDTO.label;
@ -760,8 +738,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as ArticleDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as ArticleDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as ArticleDTO).type = sectionDTO.type; (sectionDetailDTO as ArticleDTO).type = sectionDTO.type;
(sectionDetailDTO as ArticleDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as ArticleDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as ArticleDTO).configurationId = (sectionDetailDTO as ArticleDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as ArticleDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as ArticleDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as ArticleDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as ArticleDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as ArticleDTO).label = sectionDTO.label; (sectionDetailDTO as ArticleDTO).label = sectionDTO.label;
@ -781,8 +758,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as PdfDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as PdfDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as PdfDTO).type = sectionDTO.type; (sectionDetailDTO as PdfDTO).type = sectionDTO.type;
(sectionDetailDTO as PdfDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as PdfDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as PdfDTO).configurationId = (sectionDetailDTO as PdfDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as PdfDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as PdfDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as PdfDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as PdfDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as PdfDTO).label = sectionDTO.label; (sectionDetailDTO as PdfDTO).label = sectionDTO.label;
@ -802,8 +778,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as GameDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as GameDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as GameDTO).type = sectionDTO.type; (sectionDetailDTO as GameDTO).type = sectionDTO.type;
(sectionDetailDTO as GameDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as GameDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as GameDTO).configurationId = (sectionDetailDTO as GameDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as GameDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as GameDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as GameDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as GameDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as GameDTO).label = sectionDTO.label; (sectionDetailDTO as GameDTO).label = sectionDTO.label;
@ -823,8 +798,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as AgendaDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as AgendaDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as AgendaDTO).type = sectionDTO.type; (sectionDetailDTO as AgendaDTO).type = sectionDTO.type;
(sectionDetailDTO as AgendaDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as AgendaDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as AgendaDTO).configurationId = (sectionDetailDTO as AgendaDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as AgendaDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as AgendaDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as AgendaDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as AgendaDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as AgendaDTO).label = sectionDTO.label; (sectionDetailDTO as AgendaDTO).label = sectionDTO.label;
@ -844,8 +818,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
(sectionDetailDTO as WeatherDTO).dateCreation = sectionDTO.dateCreation; (sectionDetailDTO as WeatherDTO).dateCreation = sectionDTO.dateCreation;
(sectionDetailDTO as WeatherDTO).type = sectionDTO.type; (sectionDetailDTO as WeatherDTO).type = sectionDTO.type;
(sectionDetailDTO as WeatherDTO).instanceId = sectionDTO.instanceId; (sectionDetailDTO as WeatherDTO).instanceId = sectionDTO.instanceId;
(sectionDetailDTO as WeatherDTO).configurationId = (sectionDetailDTO as WeatherDTO).configurationId = sectionDTO.configurationId;
sectionDTO.configurationId;
(sectionDetailDTO as WeatherDTO).isSubSection = sectionDTO.isSubSection; (sectionDetailDTO as WeatherDTO).isSubSection = sectionDTO.isSubSection;
(sectionDetailDTO as WeatherDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as WeatherDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as WeatherDTO).label = sectionDTO.label; (sectionDetailDTO as WeatherDTO).label = sectionDTO.label;
@ -862,29 +835,22 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
case SectionType.Event: case SectionType.Event:
(sectionDetailDTO as SectionEventDTO).id = sectionDTO.id; (sectionDetailDTO as SectionEventDTO).id = sectionDTO.id;
(sectionDetailDTO as SectionEventDTO).order = sectionDTO.order; (sectionDetailDTO as SectionEventDTO).order = sectionDTO.order;
(sectionDetailDTO as SectionEventDTO).dateCreation = (sectionDetailDTO as SectionEventDTO).dateCreation = sectionDTO.dateCreation;
sectionDTO.dateCreation;
(sectionDetailDTO as SectionEventDTO).type = sectionDTO.type; (sectionDetailDTO as SectionEventDTO).type = sectionDTO.type;
(sectionDetailDTO as SectionEventDTO).instanceId = (sectionDetailDTO as SectionEventDTO).instanceId = sectionDTO.instanceId;
sectionDTO.instanceId; (sectionDetailDTO as SectionEventDTO).configurationId = sectionDTO.configurationId;
(sectionDetailDTO as SectionEventDTO).configurationId = (sectionDetailDTO as SectionEventDTO).isSubSection = sectionDTO.isSubSection;
sectionDTO.configurationId;
(sectionDetailDTO as SectionEventDTO).isSubSection =
sectionDTO.isSubSection;
(sectionDetailDTO as SectionEventDTO).parentId = sectionDTO.parentId; (sectionDetailDTO as SectionEventDTO).parentId = sectionDTO.parentId;
(sectionDetailDTO as SectionEventDTO).label = sectionDTO.label; (sectionDetailDTO as SectionEventDTO).label = sectionDTO.label;
(sectionDetailDTO as SectionEventDTO).title = sectionDTO.title; (sectionDetailDTO as SectionEventDTO).title = sectionDTO.title;
(sectionDetailDTO as SectionEventDTO).description = (sectionDetailDTO as SectionEventDTO).description = sectionDTO.description;
sectionDTO.description;
(sectionDetailDTO as SectionEventDTO).imageId = sectionDTO.imageId; (sectionDetailDTO as SectionEventDTO).imageId = sectionDTO.imageId;
(sectionDetailDTO as SectionEventDTO).imageSource = (sectionDetailDTO as SectionEventDTO).imageSource = sectionDTO.imageSource;
sectionDTO.imageSource;
(sectionDetailDTO as SectionEventDTO).isBeacon = sectionDTO.isBeacon; (sectionDetailDTO as SectionEventDTO).isBeacon = sectionDTO.isBeacon;
(sectionDetailDTO as SectionEventDTO).beaconId = sectionDTO.beaconId; (sectionDetailDTO as SectionEventDTO).beaconId = sectionDTO.beaconId;
(sectionDetailDTO as SectionEventDTO).latitude = sectionDTO.latitude; (sectionDetailDTO as SectionEventDTO).latitude = sectionDTO.latitude;
(sectionDetailDTO as SectionEventDTO).longitude = sectionDTO.longitude; (sectionDetailDTO as SectionEventDTO).longitude = sectionDTO.longitude;
(sectionDetailDTO as SectionEventDTO).meterZoneGPS = (sectionDetailDTO as SectionEventDTO).meterZoneGPS = sectionDTO.meterZoneGPS;
sectionDTO.meterZoneGPS;
break; break;
} }
} }

View File

@ -1,23 +1,17 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:manager_app/Components/check_input_container.dart'; import 'package:manager_app/Components/check_input_container.dart';
import 'package:manager_app/Components/color_picker_input_container.dart';
import 'package:manager_app/Components/confirmation_dialog.dart'; import 'package:manager_app/Components/confirmation_dialog.dart';
import 'package:manager_app/Components/number_input_container.dart';
import 'package:manager_app/Components/resource_input_container.dart'; import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_app/Components/common_loader.dart'; import 'package:manager_app/Components/common_loader.dart';
import 'package:manager_app/Components/message_notification.dart'; import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/l10n/app_localizations.dart'; import 'package:manager_app/l10n/app_localizations.dart';
import 'package:manager_app/Components/multi_select_dropdown_language_container.dart'; import 'package:manager_app/Components/multi_select_dropdown_language_container.dart';
import 'package:manager_app/Components/multi_string_input_container.dart';
import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Components/string_input_container.dart'; import 'package:manager_app/Components/string_input_container.dart';
import 'package:manager_app/Helpers/FileHelper.dart'; import 'package:manager_app/Helpers/FileHelper.dart';
import 'package:manager_app/Helpers/PDFHelper.dart';
import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/Screens/Configurations/section_reorderList.dart'; import 'package:manager_app/Screens/Configurations/section_reorderList.dart';
import 'package:manager_app/app_context.dart'; import 'package:manager_app/app_context.dart';
@ -38,450 +32,387 @@ class ConfigurationDetailScreen extends StatefulWidget {
class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> { class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
ConfigurationDTO? configurationDTO; ConfigurationDTO? configurationDTO;
SectionDTO? selectedSection;
List<SectionDTO>? sections; List<SectionDTO>? sections;
Future<ConfigurationDTO?>? _configFuture;
Future<List<SectionDTO>?>? _sectionsFuture;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context); final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size; final managerCtx = appContext.getContext() as ManagerAppContext;
return FutureBuilder(
future: getConfiguration(this.widget, (appContext.getContext() as ManagerAppContext).clientAPI!), _configFuture ??= getConfiguration(widget, managerCtx.clientAPI!);
builder: (context, AsyncSnapshot<dynamic> snapshot) {
return FutureBuilder<ConfigurationDTO?>(
future: _configFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
return bodyConfiguration(snapshot.data, size, appContext, context); if (snapshot.data == null) return Center(child: Text(AppLocalizations.of(context)!.noData));
return _buildBody(snapshot.data!, appContext);
} else if (snapshot.connectionState == ConnectionState.none) { } else if (snapshot.connectionState == ConnectionState.none) {
return Text(AppLocalizations.of(context)!.noData); return Center(child: Text(AppLocalizations.of(context)!.noData));
} else {
return Center(
child: Container(
height: size.height * 0.2,
child: CommonLoader()
)
);
}
} }
return const Center(child: CommonLoader());
},
); );
} }
Widget bodyConfiguration(ConfigurationDTO configurationDTO, Size size, AppContext appContext, BuildContext context) { Widget _buildBody(ConfigurationDTO config, AppContext appContext) {
ManagerAppContext managerAppContext = appContext.getContext(); final managerCtx = appContext.getContext() as ManagerAppContext;
final canEdit = managerCtx.canEdit;
final l = AppLocalizations.of(context)!;
return Column( return Column(
//mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Container( _buildHeader(config, appContext, l),
//color: Colors.cyanAccent, Expanded(
height: size.height *0.1,
child: SingleChildScrollView( child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
_cardGeneral(config, l),
const SizedBox(height: 12),
_cardImages(config, l),
const SizedBox(height: 12),
_cardSections(config, appContext),
],
),
),
),
_buildFooter(config, appContext, canEdit, l),
],
);
}
// Header
Widget _buildHeader(ConfigurationDTO config, AppContext appContext, AppLocalizations l) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: kWhite,
border: Border(bottom: BorderSide(color: kSecond, width: 1)),
),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Align( IconButton(
alignment: AlignmentDirectional.bottomStart, icon: const Icon(Icons.arrow_back, color: kPrimaryColor),
child: Padding( onPressed: () {
padding: const EdgeInsets.all(10.0), ManagerAppContext ctx = appContext.getContext();
ctx.selectedConfiguration = null;
appContext.setContext(ctx);
},
),
const SizedBox(width: 8),
Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Text(
children: [ config.label ?? '',
Text(configurationDTO.label!, style: TextStyle(fontSize: 30, fontWeight: FontWeight.w400)), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
InkWell( overflow: TextOverflow.ellipsis,
onTap: () async { ),
try { Text(
// Export config DateFormat('dd/MM/yyyy').format(config.dateCreation!),
Client clientAPI = (appContext.getContext() as ManagerAppContext).clientAPI!; style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w300, color: Colors.grey),
ExportConfigurationDTO export = await clientAPI.configurationApi!.configurationExport(configurationDTO.id!); ),
],
),
),
IconButton(
icon: const Icon(Icons.download_outlined, color: kPrimaryColor),
tooltip: l.configExportSuccess(''),
onPressed: () => _export(config, appContext),
),
],
),
);
}
if (kIsWeb) { // Footer
html.AnchorElement anchorElement = new html.AnchorElement();
var uri = (Uri.parse((appContext.getContext() as ManagerAppContext).clientAPI!.resourceApi!.apiClient.basePath+'/api/Configuration/${configurationDTO.id}/export')); Widget _buildFooter(ConfigurationDTO config, AppContext appContext, bool canEdit, AppLocalizations l) {
anchorElement.href = uri.toString(); return Container(
anchorElement.download = '${configurationDTO.label}.json'; padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
anchorElement.click(); decoration: const BoxDecoration(
} else { color: kWhite,
File test = await FileHelper().storeConfiguration(export); boxShadow: [BoxShadow(color: kSecond, blurRadius: 8, offset: Offset(0, -2))],
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configExportSuccess(test.path), context, 3000); ),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
OutlinedButton.icon(
icon: const Icon(Icons.undo, size: 16),
label: Text(l.cancel),
onPressed: () => cancel(config, appContext),
),
if (canEdit) ...[
const SizedBox(width: 8),
ElevatedButton.icon(
icon: const Icon(Icons.delete, size: 16, color: kWhite),
label: Text(l.delete, style: const TextStyle(color: kWhite)),
style: ElevatedButton.styleFrom(backgroundColor: kError),
onPressed: () => delete(config, appContext),
),
const SizedBox(width: 8),
ElevatedButton.icon(
icon: const Icon(Icons.done, size: 16, color: kWhite),
label: Text(l.save, style: const TextStyle(color: kWhite)),
style: ElevatedButton.styleFrom(backgroundColor: kPrimaryColor),
onPressed: () => save(config, appContext),
),
],
],
),
);
} }
} catch(e) {
log(e.toString()); // Card Général
showNotification(kPrimaryColor, kWhite, AppLocalizations.of(context)!.configExportFailed, context, null);
} Widget _cardGeneral(ConfigurationDTO config, AppLocalizations l) {
}, return Card(
elevation: 0,
color: kWhite,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 5.0), padding: const EdgeInsets.all(16),
child: Icon(Icons.cloud_download, color: kPrimaryColor)
),
),
],
),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(DateFormat('dd/MM/yyyy').format(configurationDTO.dateCreation!), style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200)),
),
],
),
)
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Align(
alignment: AlignmentDirectional.centerEnd,
child: InkWell(
onTap: () {
ManagerAppContext managerAppContext = appContext.getContext();
managerAppContext.selectedConfiguration = null;
appContext.setContext(managerAppContext);
},
child: Container(
child: Icon(
Icons.arrow_back,
color: kPrimaryColor,
size: 50.0,
)
)
),
),
)
],
),
),
), // TITLE
Container(
height: size.height *0.78,
//color: Colors.red,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
LayoutBuilder( Text(l.identifierLabel.replaceAll(':', '').trim(),
builder: (context, constraints) { style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: kTitleTextColor)),
final isWide = constraints.maxWidth > 700; const SizedBox(height: 12),
StringInputContainer(
final fields = Column( label: l.identifierLabel,
crossAxisAlignment: CrossAxisAlignment.start, fontSize: 16,
children: [ fontSizeText: 16,
SizedBox( initialValue: config.label,
height: 70, onChanged: (value) => config.label = value,
child: StringInputContainer(
label: AppLocalizations.of(context)!.identifierLabel,
fontSize: 20,
fontSizeText: 20,
initialValue: configurationDTO.label,
onChanged: (value) {
configurationDTO.label = value;
},
),
), ),
const SizedBox(height: 12),
Wrap( Wrap(
spacing: 24, spacing: 24,
runSpacing: 8, runSpacing: 12,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
MultiSelectDropdownLanguageContainer( MultiSelectDropdownLanguageContainer(
label: AppLocalizations.of(context)!.languagesLabel, label: l.languagesLabel,
initialValue: configurationDTO.languages != null ? configurationDTO.languages! : [], initialValue: config.languages ?? [],
values: languages, values: languages,
isMultiple: true, isMultiple: true,
fontSize: 20, fontSize: 16,
isAtLeastOne: true, isAtLeastOne: true,
onChanged: (value) { onChanged: (value) {
var tempOutput = new List<String>.from(value); config.languages = List<String>.from(value);
configurationDTO.languages = tempOutput;
}, },
), ),
CheckInputContainer( CheckInputContainer(
icon: Icons.signal_wifi_off, icon: Icons.signal_wifi_off,
label: "Hors ligne :", // no ARB key for this one yet, leave as-is label: "Hors ligne :",
fontSize: 20, fontSize: 16,
isChecked: configurationDTO.isOffline, isChecked: config.isOffline,
onChanged: (value) { onChanged: (value) => config.isOffline = value,
configurationDTO.isOffline = value;
},
), ),
], ],
), ),
], ],
),
),
); );
}
final images = Column( // Card Images
Widget _cardImages(ConfigurationDTO config, AppLocalizations l) {
return Card(
elevation: 0,
color: kWhite,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text("Images",
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: kTitleTextColor)),
const SizedBox(height: 12),
LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth > kBreakpointMobile;
final children = [
ResourceInputContainer( ResourceInputContainer(
label: AppLocalizations.of(context)!.mainImageLabel, label: l.mainImageLabel,
fontSize: 20, fontSize: 16,
initialValue: configurationDTO.imageId, initialValue: config.imageId,
color: kPrimaryColor, color: kPrimaryColor,
onChanged: (ResourceDTO resource) { onChanged: (ResourceDTO resource) {
if (resource.id == null) { if (resource.id == null) {
configurationDTO.imageId = null; config.imageId = null;
configurationDTO.imageSource = null; config.imageSource = null;
} else { } else {
configurationDTO.imageId = resource.id; config.imageId = resource.id;
configurationDTO.imageSource = resource.url; config.imageSource = resource.url;
} }
}, },
), ),
ResourceInputContainer( ResourceInputContainer(
label: AppLocalizations.of(context)!.loaderLabel, label: l.loaderLabel,
fontSize: 20, fontSize: 16,
initialValue: configurationDTO.loaderImageId, initialValue: config.loaderImageId,
color: kPrimaryColor, color: kPrimaryColor,
onChanged: (ResourceDTO resource) { onChanged: (ResourceDTO resource) {
if (resource.id == null) { if (resource.id == null) {
configurationDTO.loaderImageId = null; config.loaderImageId = null;
configurationDTO.loaderImageUrl = null; config.loaderImageUrl = null;
} else { } else {
configurationDTO.loaderImageId = resource.id; config.loaderImageId = resource.id;
configurationDTO.loaderImageUrl = resource.url; config.loaderImageUrl = resource.url;
} }
}, },
), ),
], ];
);
if (isWide) { if (isWide) {
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: children.map((c) => Expanded(child: Padding(
Expanded(flex: 3, child: fields), padding: const EdgeInsets.only(right: 16),
SizedBox(width: 24), child: c,
Expanded(flex: 2, child: images), ))).toList(),
],
); );
} else { }
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: children.map((c) => Padding(
SizedBox( padding: const EdgeInsets.only(bottom: 12),
height: 70, child: c,
child: StringInputContainer( )).toList(),
label: "Identifiant :",
fontSize: 20,
fontSizeText: 20,
initialValue: configurationDTO.label,
onChanged: (value) {
configurationDTO.label = value;
},
),
),
SizedBox(height: 8),
MultiSelectDropdownLanguageContainer(
label: "Langues :",
initialValue: configurationDTO.languages != null ? configurationDTO.languages! : [],
values: languages,
isMultiple: true,
fontSize: 20,
isAtLeastOne: true,
onChanged: (value) {
var tempOutput = new List<String>.from(value);
configurationDTO.languages = tempOutput;
},
),
SizedBox(height: 8),
CheckInputContainer(
icon: Icons.signal_wifi_off,
label: "Hors ligne :",
fontSize: 20,
isChecked: configurationDTO.isOffline,
onChanged: (value) {
configurationDTO.isOffline = value;
},
),
SizedBox(height: 16),
images,
],
); );
}
}, },
), ),
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(25.0),
border: Border.all(width: 0.5, color: kSecond)
),
child: FutureBuilder(
future: getSections(configurationDTO, (appContext.getContext() as ManagerAppContext).clientAPI!),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done)
{
sections = new List<SectionDTO>.from(snapshot.data).where((section) => !section.isSubSection!).toList();
return bodyGrid(configurationDTO, size, appContext);
}
else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
} else {
return Center(
child: Container(
height: size.height * 0.15,
child: CommonLoader()
)
);
}
}
),
),
),
], ],
), ),
), ),
),
),
),// FIELDS SECTION
Container(
height: size.height *0.08,
//color: Colors.greenAccent,
child: SingleChildScrollView(
child: getButtons(configurationDTO, appContext)
)
)
],
);
}
getButtons(ConfigurationDTO configurationDTO, AppContext appContext) {
final canEdit = (appContext.getContext() as ManagerAppContext).canEdit;
return Align(
alignment: AlignmentDirectional.bottomCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(5.0),
child: RoundedButton(
text: AppLocalizations.of(context)!.cancel,
icon: Icons.undo,
color: Colors.grey,
textColor: Colors.white,
fontSize: 15,
press: () {
cancel(configurationDTO, appContext);
},
),
),
if (canEdit) Padding(
padding: const EdgeInsets.all(5.0),
child: RoundedButton(
text: AppLocalizations.of(context)!.delete,
icon: Icons.delete,
color: kError,
textColor: Colors.white,
fontSize: 15,
press: () {
delete(configurationDTO, appContext);
},
),
),
if (canEdit) Padding(
padding: const EdgeInsets.all(5.0),
child: RoundedButton(
text: AppLocalizations.of(context)!.save,
icon: Icons.done,
color: kSuccess,
textColor: Colors.white,
fontSize: 15,
press: () {
save(configurationDTO, appContext);
},
),
),
],
),
); );
} }
Widget bodyGrid(ConfigurationDTO configurationDTO, Size size, AppContext appContext) { // Card Sections
return SingleChildScrollView(
child: Container( Widget _cardSections(ConfigurationDTO config, AppContext appContext) {
height: size.height *0.40, final managerCtx = appContext.getContext() as ManagerAppContext;
_sectionsFuture ??= getSections(config, managerCtx.clientAPI!);
return Card(
elevation: 0,
color: kWhite,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(16),
child: SectionReorderList( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Sections",
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: kTitleTextColor)),
const SizedBox(height: 12),
FutureBuilder<List<SectionDTO>?>(
future: _sectionsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) return const Text("No data");
sections = List<SectionDTO>.from(snapshot.data!)
.where((s) => !s.isSubSection!)
.toList();
return SectionReorderList(
sectionsIn: sections!, sectionsIn: sections!,
configurationId: configurationDTO.id!, configurationId: config.id!,
onChangedOrder: (List<SectionDTO> sectionsOut) async { onChangedOrder: (List<SectionDTO> sectionsOut) async {
sections = sectionsOut; sections = sectionsOut;
await managerCtx.clientAPI!.sectionApi!.sectionUpdateOrder(sections!);
// Update section order
ManagerAppContext managerAppContext = appContext.getContext();
await managerAppContext.clientAPI!.sectionApi!.sectionUpdateOrder(sections!);
}, },
askReload: () { askReload: () => setState(() {
setState(() { _sectionsFuture = null;
// refresh UI }),
}); );
} else if (snapshot.connectionState == ConnectionState.none) {
return const Text("No data");
}
return const Center(child: Padding(
padding: EdgeInsets.all(32),
child: CommonLoader(),
));
}, },
), ),
],
), ),
), ),
); );
} }
Future<void> cancel(ConfigurationDTO configurationDTO, AppContext appContext) async { // Actions
void _export(ConfigurationDTO config, AppContext appContext) async {
final l = AppLocalizations.of(context)!;
try {
Client clientAPI = (appContext.getContext() as ManagerAppContext).clientAPI!;
await clientAPI.configurationApi!.configurationExport(config.id!);
if (kIsWeb) {
html.AnchorElement anchorElement = html.AnchorElement();
var uri = Uri.parse('${clientAPI.resourceApi!.apiClient.basePath}/api/Configuration/${config.id}/export');
anchorElement.href = uri.toString();
anchorElement.download = '${config.label}.json';
anchorElement.click();
} else {
ExportConfigurationDTO export = await clientAPI.configurationApi!.configurationExport(config.id!);
File test = await FileHelper().storeConfiguration(export);
showNotification(kSuccess, kWhite, l.configExportSuccess(test.path), context, 3000);
}
} catch (e) {
log(e.toString());
showNotification(kPrimaryColor, kWhite, l.configExportFailed, context, null);
}
}
Future<void> cancel(ConfigurationDTO config, AppContext appContext) async {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
ConfigurationDTO? configuration = await managerAppContext.clientAPI!.configurationApi!.configurationGetDetail(configurationDTO.id!); ConfigurationDTO? configuration = await managerAppContext.clientAPI!.configurationApi!.configurationGetDetail(config.id!);
managerAppContext.selectedConfiguration = configuration; managerAppContext.selectedConfiguration = configuration;
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
} }
Future<void> delete(ConfigurationDTO configurationDTO, AppContext appContext) async { Future<void> delete(ConfigurationDTO config, AppContext appContext) async {
showConfirmationDialog( showConfirmationDialog(
AppLocalizations.of(context)!.configDeleteConfirm, AppLocalizations.of(context)!.configDeleteConfirm,
() {}, () {},
() async { () async {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
await managerAppContext.clientAPI!.configurationApi!.configurationDelete(configurationDTO.id!); await managerAppContext.clientAPI!.configurationApi!.configurationDelete(config.id!);
managerAppContext.selectedConfiguration = null; managerAppContext.selectedConfiguration = null;
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configDeletedSuccess, context, null); showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configDeletedSuccess, context, null);
}, },
context context,
); );
} }
Future<void> save(ConfigurationDTO configurationDTO, AppContext appContext) async { Future<void> save(ConfigurationDTO config, AppContext appContext) async {
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
ConfigurationDTO? configuration = await managerAppContext.clientAPI!.configurationApi!.configurationUpdate(configurationDTO); ConfigurationDTO? configuration = await managerAppContext.clientAPI!.configurationApi!.configurationUpdate(config);
managerAppContext.selectedConfiguration = configuration; managerAppContext.selectedConfiguration = configuration;
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configSavedSuccess, context, null); showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configSavedSuccess, context, null);
} }
Future<ConfigurationDTO?> getConfiguration(ConfigurationDetailScreen widget, Client client) async { Future<ConfigurationDTO?> getConfiguration(ConfigurationDetailScreen widget, Client client) async {
ConfigurationDTO? configuration = await client.configurationApi!.configurationGetDetail(widget.id); return await client.configurationApi!.configurationGetDetail(widget.id);
return configuration;
} }
Future<List<SectionDTO>?> getSections(ConfigurationDTO configurationDTO, Client client) async { Future<List<SectionDTO>?> getSections(ConfigurationDTO config, Client client) async {
List<SectionDTO>? sections = await client.sectionApi!.sectionGetFromConfiguration(configurationDTO.id!); List<SectionDTO>? sections = await client.sectionApi!.sectionGetFromConfiguration(config.id!);
if(sections != null) { if (sections != null) {
sections.sort((a, b) => a.order!.compareTo(b.order!)); sections.sort((a, b) => a.order!.compareTo(b.order!));
} }
return sections; return sections;
} }
} }
boxDecoration(dynamic element) {
return BoxDecoration(
color: element.id == null ? kSuccess : kTextLightColor,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(25.0),
boxShadow: [
BoxShadow(
color: kSecond,
spreadRadius: 0.5,
blurRadius: 5,
offset: Offset(0, 1.5), // changes position of shadow
),
],
);
}

View File

@ -1,6 +1,7 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:manager_app/Components/common_loader.dart'; import 'package:manager_app/Components/common_loader.dart';
import 'package:manager_app/Components/string_input_container.dart';
import 'package:manager_app/l10n/app_localizations.dart'; import 'package:manager_app/l10n/app_localizations.dart';
import 'package:manager_app/Components/message_notification.dart'; import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Models/managerContext.dart';
@ -23,255 +24,249 @@ class ConfigurationsScreen extends StatefulWidget {
class _ConfigurationsScreenState extends State<ConfigurationsScreen> { class _ConfigurationsScreenState extends State<ConfigurationsScreen> {
ConfigurationDTO? selectedConfiguration; ConfigurationDTO? selectedConfiguration;
String _filter = '';
Future<List<ConfigurationDTO>?>? _future;
void _refresh() => setState(() => _future = null);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context); final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size; final Size size = MediaQuery.of(context).size;
final ManagerAppContext managerAppContext = appContext.getContext();
ManagerAppContext managerAppContext = appContext.getContext();
if (managerAppContext.selectedSection != null) { if (managerAppContext.selectedSection != null) {
return SectionDetailScreen(id: managerAppContext.selectedSection!.id!); return SectionDetailScreen(id: managerAppContext.selectedSection!.id!);
} }
if (managerAppContext.selectedConfiguration != null) { if (managerAppContext.selectedConfiguration != null) {
return ConfigurationDetailScreen(id: managerAppContext.selectedConfiguration!.id!); return ConfigurationDetailScreen(id: managerAppContext.selectedConfiguration!.id!);
} else { }
_future ??= getConfigurations(managerAppContext);
return Align( return Align(
alignment: AlignmentDirectional.topCenter, alignment: AlignmentDirectional.topCenter,
child: FutureBuilder( child: FutureBuilder(
future: getConfigurations(managerAppContext), future: _future,
builder: (context, AsyncSnapshot<dynamic> snapshot) { builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
var tempOutput = new List<ConfigurationDTO>.from(snapshot.data); var all = List<ConfigurationDTO>.from(snapshot.data);
if (managerAppContext.canEdit) tempOutput.add(ConfigurationDTO(id: null)); if (managerAppContext.canEdit) all.add(ConfigurationDTO(id: null));
return bodyGrid(tempOutput, size, appContext, context);
final filtered = _filter.isEmpty
? all
: all
.where((c) =>
c.id == null ||
(c.label?.toLowerCase().contains(_filter.toLowerCase()) ??
false))
.toList();
return _buildGrid(filtered, size, appContext, context);
} else if (snapshot.connectionState == ConnectionState.none) { } else if (snapshot.connectionState == ConnectionState.none) {
return Text(AppLocalizations.of(context)!.noData); return Text(AppLocalizations.of(context)!.noData);
} else { } else {
return Center( return Center(
child: Container( child: Container(
height: size.height * 0.2, height: size.height * 0.2,
child: CommonLoader() child: CommonLoader(),
)
);
}
}
), ),
); );
} }
}
Widget bodyGrid(data, Size size, AppContext appContext, BuildContext mainContext) {
final screenWidth = MediaQuery.of(context).size.width;
final itemWidth = 175;
final crossAxisCount = (screenWidth / itemWidth).floor().clamp(1, 6);
return GridView.builder(
shrinkWrap: true,
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: crossAxisCount),
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return
InkWell(
onTap: () async {
if (data[index].id == null) {
var configurationToCreate = await showNewConfiguration(appContext, (bool) {
if (bool) {
setState(() {
// Thanks future builder for the refresh..
});
}
}, context, mainContext);
if(configurationToCreate != null) {
if (configurationToCreate.label != null) {
configurationToCreate.dateCreation = DateTime.now();
/*configurationToCreate.isMobile = false;
configurationToCreate.isTablet = false;*/
configurationToCreate.isOffline = false;
/*configurationToCreate.isDate = false;
configurationToCreate.isHour = false;
configurationToCreate.isSectionImageBackground = false;*/
configurationToCreate.instanceId = (appContext.getContext() as ManagerAppContext).instanceId;
await (appContext.getContext() as ManagerAppContext).clientAPI!.configurationApi!.configurationCreate(configurationToCreate);
ManagerAppContext managerAppContext = appContext.getContext();
managerAppContext.selectedConfiguration = null;
await appContext.setContext(managerAppContext);
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configCreatedSuccess, context, null);
}
}
} else {
setState(() {
ManagerAppContext managerAppContext = appContext.getContext();
managerAppContext.selectedConfiguration = data[index];
selectedConfiguration = data[index];
});
}
}, },
child: Container(
decoration: boxDecoration(data[index]),
padding: const EdgeInsets.all(15),
margin: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
child: Align(
alignment: Alignment.center,
child: getElement(data[index], size)
), ),
),
);
}
); );
} }
getElement(ConfigurationDTO configuration, Size size) { Widget _buildGrid(List<ConfigurationDTO> data, Size size, AppContext appContext,
if (configuration.id != null) { BuildContext mainContext) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Align( Padding(
padding: const EdgeInsets.fromLTRB(12, 12, 12, 4),
child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: AutoSizeText( child: SizedBox(
configuration.label!, width: 280,
style: new TextStyle(fontSize: 25), child: StringInputContainer(
maxLines: 1, label: AppLocalizations.of(context)!.searchLabel,
onChanged: (v) => setState(() => _filter = v),
), ),
), ),
Align( ),
alignment: Alignment.bottomRight, ),
child: AutoSizeText( Expanded(
DateFormat('dd/MM/yyyy').format(configuration.dateCreation!), child: GridView.builder(
style: new TextStyle(fontSize: 18, fontFamily: ""), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
maxLines: 1, gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 220,
childAspectRatio: 1.5,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return _ConfigCard(
config: data[index],
onTap: () => _onTap(data[index], appContext, mainContext),
);
},
), ),
), ),
], ],
); );
} else {
return Icon(
Icons.add,
color: kTextLightColor,
size: size.height*0.1,
);
} }
}
}
Future<List<ConfigurationDTO>?> getConfigurations(ManagerAppContext managerAppContext) async { Future<void> _onTap(
List<ConfigurationDTO>? configurations = await managerAppContext.clientAPI!.configurationApi!.configurationGet(instanceId: managerAppContext.instanceId); ConfigurationDTO config, AppContext appContext, BuildContext mainContext) async {
//print("number of configurations " + configurations.length.toString()); if (config.id == null) {
if(configurations != null) { var configurationToCreate = await showNewConfiguration(
configurations.forEach((element) { appContext,
//print(element); (bool refresh) {
if (refresh) _refresh();
},
context,
mainContext,
);
if (configurationToCreate != null && configurationToCreate.label != null) {
configurationToCreate.dateCreation = DateTime.now();
configurationToCreate.isOffline = false;
configurationToCreate.instanceId =
(appContext.getContext() as ManagerAppContext).instanceId;
await (appContext.getContext() as ManagerAppContext)
.clientAPI!
.configurationApi!
.configurationCreate(configurationToCreate);
final ctx = appContext.getContext() as ManagerAppContext;
ctx.selectedConfiguration = null;
await appContext.setContext(ctx);
showNotification(
kSuccess, kWhite, AppLocalizations.of(context)!.configCreatedSuccess, context, null);
_refresh();
}
} else {
setState(() {
final ctx = appContext.getContext() as ManagerAppContext;
ctx.selectedConfiguration = config;
selectedConfiguration = config;
}); });
} }
}
return configurations;
} }
boxDecoration(ConfigurationDTO configurationDTO) { class _ConfigCard extends StatefulWidget {
return BoxDecoration( final ConfigurationDTO config;
color: configurationDTO.id == null ? kSuccess : kTextLightColor, final VoidCallback onTap;
shape: BoxShape.rectangle, const _ConfigCard({required this.config, required this.onTap});
borderRadius: BorderRadius.circular(25.0),
image: configurationDTO.imageSource != null ? new DecorationImage( @override
fit: BoxFit.cover, State<_ConfigCard> createState() => _ConfigCardState();
colorFilter: new ColorFilter.mode(Colors.black.withOpacity(0.18), BlendMode.dstATop), }
image: new NetworkImage(
configurationDTO.imageSource!, class _ConfigCardState extends State<_ConfigCard> {
), bool _hovered = false;
) : null,
@override
Widget build(BuildContext context) {
final config = widget.config;
final isAdd = config.id == null;
final hasImage = config.imageSource != null;
return MouseRegion(
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: GestureDetector(
onTap: widget.onTap,
child: AnimatedScale(
scale: _hovered ? 1.03 : 1.0,
duration: const Duration(milliseconds: 150),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: kSecond, color: kSecond,
spreadRadius: 0.5, spreadRadius: _hovered ? 1 : 0.5,
blurRadius: 5, blurRadius: _hovered ? 10 : 5,
offset: Offset(0, 1.5), // changes position of shadow offset: const Offset(0, 2),
), ),
], ],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Stack(
fit: StackFit.expand,
children: [
// Background
if (!isAdd && hasImage)
Image.network(config.imageSource!, fit: BoxFit.cover)
else
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: isAdd
? [kSuccess, kSuccess.withOpacity(0.7)]
: [kPrimaryColor, kPrimaryColor.withOpacity(0.6)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
// Gradient scrim
if (!isAdd)
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: const [0.35, 1.0],
colors: [
Colors.transparent,
Colors.black.withOpacity(0.65),
],
),
),
),
// Content
if (isAdd)
const Center(
child: Icon(Icons.add, color: kTextLightColor, size: 36),
)
else
Positioned(
left: 10,
right: 10,
bottom: 10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
AutoSizeText(
config.label ?? '',
style: kCardTitleStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (config.dateCreation != null)
Text(
DateFormat('dd/MM/yyyy').format(config.dateCreation!),
style: kCardSubtitleStyle,
),
],
),
),
],
),
),
),
),
),
); );
}
} }
Future<List<ConfigurationDTO>?> getConfigurations(
/*Widget bodyTable(data) => DataTable( ManagerAppContext managerAppContext) async {
sortColumnIndex: 0, return managerAppContext.clientAPI!.configurationApi!
sortAscending: true, .configurationGet(instanceId: managerAppContext.instanceId);
columns: <DataColumn>[ }
DataColumn(
label: Text("Label"),
onSort: (_, __) {
setState(() {
// data.sort((a) => a.label);
});
},
),
DataColumn(
label: Text("Primary color"),
onSort: (_, __) {
setState(() {
/*data.sort((a, b) => a.data["stats"]["dividendYield"]
.compareTo(b.data["stats"]["dividendYield"]));*/
});
},
),
DataColumn(
label: Text("Secondary color"),
onSort: (_, __) {
setState(() {
/*data.sort((a, b) => a.data["quote"]["iexBidPrice"]
.compareTo(b.data["quote"]["iexBidPrice"]));*/
});
},
),
DataColumn(
label: Text("Languages"),
onSort: (_, __) {
setState(() {
/*data.sort((a, b) => a.data["stats"]["latestPrice"]
.compareTo(b.data["stats"]["latestPrice"]));*/
});
},
),
DataColumn(
label: Text("Date creation"),
onSort: (_, __) {
setState(() {
/*data.sort((a, b) => a.data["stats"]["latestPrice"]
.compareTo(b.data["stats"]["latestPrice"]));*/
});
},
),
DataColumn(
label: Text("Actions"),
onSort: (_, __) {
setState(() {
/*data.sort((a, b) => a.data["stats"]["latestPrice"]
.compareTo(b.data["stats"]["latestPrice"]));*/
});
},
),
],
rows: data
.map<DataRow>( (configuration) => DataRow(
cells: [
DataCell(
Text('${configuration.label ?? ""}'),
),
DataCell(
Text('${configuration.primaryColor ?? ""}'),
),
DataCell(
Text('${configuration.secondaryColor ?? ""}'),
),
DataCell(
Text('${configuration.languages ?? ""}'),
),
DataCell(
Text('${configuration.dateCreation ?? ""}'),
),
DataCell(
Text("Todo buttons - open = edit, delete"),
),
],
),
).toList()
);*/

View File

@ -14,26 +14,31 @@ import 'package:manager_api_new/api.dart';
Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChanged<bool> isImport, BuildContext context, BuildContext mainContext) { Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChanged<bool> isImport, BuildContext context, BuildContext mainContext) {
final l = AppLocalizations.of(mainContext)!; final l = AppLocalizations.of(mainContext)!;
ConfigurationDTO configurationDTO = new ConfigurationDTO(); ConfigurationDTO configurationDTO = new ConfigurationDTO();
Size size = MediaQuery.of(mainContext).size;
configurationDTO.label = ""; configurationDTO.label = "";
var configuration = showDialog<ConfigurationDTO?>( var configuration = showDialog<ConfigurationDTO?>(
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
content: SingleChildScrollView(
child: SizedBox( child: SizedBox(
width: size.width*0.35, width: 460,
height: size.height*0.3, child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min,
children: [ children: [
Center(child: Text(l.newConfiguration, style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400))), Center(child: Text(l.newConfiguration, style: new TextStyle(fontSize: 22, fontWeight: FontWeight.w400))),
Center( SizedBox(height: 8),
child: SizedBox( SizedBox(
height: 100, height: 90,
child: StringInputContainer( child: StringInputContainer(
label: l.configNameLabel, label: l.configNameLabel,
initialValue: configurationDTO.label, initialValue: configurationDTO.label,
@ -42,21 +47,19 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
}, },
), ),
), ),
), SizedBox(height: 8),
Text(l.orText), Text(l.orText),
SizedBox(height: 4),
Column( Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(bottom: 5.0), padding: const EdgeInsets.only(bottom: 5.0),
child: Text(l.importLabel, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300)), child: Text(l.importLabel, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w300)),
), ),
InkWell( InkWell(
onTap: () async { onTap: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles(); FilePickerResult? result = await FilePicker.platform.pickFiles();
//String result = filePicker();
if (result != null) { if (result != null) {
await FileHelper().importConfiguration(result, null, (appContext.getContext() as ManagerAppContext).clientAPI!, mainContext); await FileHelper().importConfiguration(result, null, (appContext.getContext() as ManagerAppContext).clientAPI!, mainContext);
isImport(true); isImport(true);
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -64,11 +67,7 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
child: Icon( child: Icon(Icons.file_upload, color: kPrimaryColor, size: 30),
Icons.file_upload,
color: kPrimaryColor,
size: 30
),
), ),
) )
] ]
@ -77,51 +76,39 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
), ),
), ),
), ),
actions: <Widget>[ const SizedBox(height: 16),
Row( Wrap(
mainAxisAlignment: MainAxisAlignment.spaceAround, spacing: 8,
runSpacing: 8,
alignment: WrapAlignment.end,
children: [ children: [
Align( RoundedButton(
alignment: AlignmentDirectional.bottomEnd,
child: Container(
width: 175,
height: 70,
child: RoundedButton(
text: l.cancel, text: l.cancel,
icon: Icons.undo, icon: Icons.undo,
color: kSecond, color: kSecond,
press: () { press: () => Navigator.of(context).pop(),
Navigator.of(context).pop(); fontSize: 16,
},
fontSize: 20,
), ),
), RoundedButton(
),
Align(
alignment: AlignmentDirectional.bottomEnd,
child: Container(
width: 150,
height: 70,
child: RoundedButton(
text: l.create, text: l.create,
icon: Icons.check, icon: Icons.check,
color: kPrimaryColor, color: kPrimaryColor,
textColor: kWhite, textColor: kWhite,
press: () { press: () {
if(configurationDTO.label != null && configurationDTO.label!.length > 2) { if(configurationDTO.label != null && configurationDTO.label!.length > 2) {
//create(configurationDTO, appContext, context);
Navigator.of(context).pop(configurationDTO); Navigator.of(context).pop(configurationDTO);
} else { } else {
showNotification(Colors.orange, kWhite, l.configNameRequired, context, null); showNotification(Colors.orange, kWhite, l.configNameRequired, context, null);
} }
}, },
fontSize: 20, fontSize: 16,
),
),
), ),
], ],
), ),
], ],
),
),
),
), context: context ), context: context
); );
@ -129,38 +116,12 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
} }
String filePicker() { String filePicker() {
/*final file = OpenFilePicker()
..filterSpecification = {
'Fichier (*.json)': '*.json',
//'Video (*.mp4)': '*.mp4',
//'All Files': '*.*'
}
..defaultFilterIndex = 0
..title = 'Sélectionner un fichier';
final result = file.getFile();*/
var result; var result;
return result != null ? result.path : null; return result != null ? result.path : null;
} }
void create(ConfigurationDTO configurationDTO, AppContext appContext, context) async { void create(ConfigurationDTO configurationDTO, AppContext appContext, context) async {
if (configurationDTO.label != null) { if (configurationDTO.label != null) {
/*configurationDTO.dateCreation = DateTime.now();
configurationDTO.isMobile = false;
configurationDTO.isTablet = false;
configurationDTO.isOffline = false;
configurationDTO.isDate = false;
configurationDTO.isHour = false;
configurationDTO.isSectionImageBackground = false;
configurationDTO.instanceId = (appContext.getContext() as ManagerAppContext).instanceId;
await (appContext.getContext() as ManagerAppContext).clientAPI!.configurationApi!.configurationCreate(configurationDTO);
ManagerAppContext managerAppContext = appContext.getContext();
managerAppContext.selectedConfiguration = null;
await appContext.setContext(managerAppContext);
showNotification(Colors.green, kWhite, 'La configuration a été créée avec succès', context, null);*/
Navigator.of(context).pop(configurationDTO); Navigator.of(context).pop(configurationDTO);
} }
} }

View File

@ -23,12 +23,20 @@ Future<SectionDTO?> showNewSection(String configurationId,
sectionDTO.type = SectionType.Map; sectionDTO.type = SectionType.Map;
var section = showDialog<SectionDTO?>( var section = showDialog<SectionDTO?>(
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0))), borderRadius: BorderRadius.all(Radius.circular(20.0))),
content: SingleChildScrollView(
child: SizedBox( child: SizedBox(
width: 750, width: 780,
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView(
child: SizedBox(
height: 250, height: 250,
child: Container( child: Container(
constraints: BoxConstraints(minHeight: 300, minWidth: 300), constraints: BoxConstraints(minHeight: 300, minWidth: 300),
@ -83,7 +91,8 @@ Future<SectionDTO?> showNewSection(String configurationId,
), ),
), ),
), ),
actions: <Widget>[ ),
const SizedBox(height: 16),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@ -136,6 +145,9 @@ Future<SectionDTO?> showNewSection(String configurationId,
), ),
], ],
), ),
),
),
),
context: contextBuild); context: contextBuild);
return section; return section;

View File

@ -62,73 +62,12 @@ class _SectionReorderListState extends State<SectionReorderList> {
final appContext = Provider.of<AppContext>(context); final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
ManagerAppContext managerAppContext = appContext.getContext(); return Column(
ConfigurationDTO currentConfiguration = managerAppContext.selectedConfiguration!; crossAxisAlignment: CrossAxisAlignment.start,
return Stack(
children: [ children: [
Padding( Row(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0, top: 15.0), children: [
child: Container( Expanded(
height: size.height*0.3,
child: ReorderableListView.builder(
itemCount: sections.length,
onReorder: _onReorder,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(vertical: 20.0),
itemBuilder: (context, index) {
//final String productName = _products[index];
return ListViewCardSections(
sections,
index,
Key('$index'),
false,
//currentConfiguration.isMobile!,
appContext,
(section) {
setState(() {
ManagerAppContext managerAppContext = appContext.getContext();
managerAppContext.selectedSection = section;
appContext.setContext(managerAppContext);
});
}
);
},
/*children: List.generate(
sections.length,
(index) {
return ListViewCardSections(
sections,
index,
Key('$index'),
appContext,
(section) {
setState(() {
ManagerAppContext managerAppContext = appContext.getContext();
managerAppContext.selectedSection = section;
appContext.setContext(managerAppContext);
});
}
);
},
),*/
),
),
),
Positioned(
top: 10,
left: 10,
child: Text(
"Sections",
style: TextStyle(fontSize: 15),
),
),
Positioned(
bottom: -20,
left: 10,
child: Container(
height: size.height*0.1,
constraints: BoxConstraints(minHeight: 85),
child: StringInputContainer( child: StringInputContainer(
label: "Recherche:", label: "Recherche:",
isSmall: true, isSmall: true,
@ -142,34 +81,23 @@ class _SectionReorderListState extends State<SectionReorderList> {
}, },
), ),
), ),
), const SizedBox(width: 8),
Positioned( InkWell(
top: 10,
right: 10,
child: InkWell(
onTap: () async { onTap: () async {
var sectionToCreate = await showNewSection(widget.configurationId, appContext, context, false); var sectionToCreate = await showNewSection(widget.configurationId, appContext, context, false);
if(sectionToCreate != null) if (sectionToCreate != null) {
{
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
sectionToCreate.instanceId = managerAppContext.instanceId; sectionToCreate.instanceId = managerAppContext.instanceId;
sectionToCreate.isBeacon = false; sectionToCreate.isBeacon = false;
sectionToCreate.dateCreation = DateTime.now(); sectionToCreate.dateCreation = DateTime.now();
SectionDTO? newSection = await managerAppContext.clientAPI!.sectionApi!.sectionCreate(sectionToCreate); await managerAppContext.clientAPI!.sectionApi!.sectionCreate(sectionToCreate);
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);
widget.askReload(); widget.askReload();
} }
}, },
child: Container( child: Container(
height: MediaQuery.of(context).size.width * 0.04, height: 36,
width: MediaQuery.of(context).size.width * 0.04, width: 36,
child: Icon(
Icons.add,
color: kTextLightColor,
size: 30.0,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: kSuccess, color: kSuccess,
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
@ -179,13 +107,47 @@ class _SectionReorderListState extends State<SectionReorderList> {
color: kSecond, color: kSecond,
spreadRadius: 0.5, spreadRadius: 0.5,
blurRadius: 5, blurRadius: 5,
offset: Offset(0, 1.5), // changes position of shadow offset: Offset(0, 1.5),
), ),
], ],
), ),
child: const Icon(
Icons.add,
color: kTextLightColor,
size: 20.0,
),
),
),
],
),
const SizedBox(height: 8),
SizedBox(
height: size.height * 0.3,
child: ReorderableListView.builder(
itemCount: sections.length,
onReorder: _onReorder,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(vertical: 8.0),
itemBuilder: (context, index) {
return ListViewCardSections(
sections,
index,
Key('$index'),
false,
appContext,
(section) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
ManagerAppContext managerAppContext = appContext.getContext();
managerAppContext.selectedSection = section;
appContext.setContext(managerAppContext);
});
});
},
);
},
), ),
), ),
)
], ],
); );
} }

View File

@ -15,102 +15,93 @@ import 'package:manager_api_new/api.dart';
import '../../Components/resource_input_container.dart'; import '../../Components/resource_input_container.dart';
import 'dropDown_configuration.dart'; import 'dropDown_configuration.dart';
showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appConfiguration, Function onGetResult, int maxLines, BuildContext mainContext, dynamic appContext) async { showChangeInfo(String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appConfiguration, Function onGetResult, int maxLines, BuildContext mainContext, dynamic appContext) async {
Size size = MediaQuery.of(mainContext).size;
var result = await showDialog( var result = await showDialog(
builder: (BuildContext context) => AlertDialog( useRootNavigator: false,
builder: (BuildContext context) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
title: Center(child: Text(text)), child: SizedBox(
content: Container( width: 580,
width: size.width * 0.5, child: Padding(
height: size.height * 0.5, padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
constraints: BoxConstraints(minWidth: 400, minHeight: 500),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Center(child: Text(text, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500))),
mainAxisAlignment: MainAxisAlignment.spaceAround, const SizedBox(height: 16),
Flexible(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( // Nom + Configuration
width: size.width * 0.3, Wrap(
height: size.height * 0.15, spacing: 16,
runSpacing: 12,
children: [
SizedBox(
width: 220,
height: 90,
child: StringInputContainer( child: StringInputContainer(
label: "Nom:", label: "Nom:",
initialValue: inputDevice.name, initialValue: inputDevice.name,
onChanged: (value) { onChanged: (value) => inputDevice.name = value,
inputDevice.name = value;
},
maxLength: 20, maxLength: 20,
), ),
), ),
Row( Row(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Align( Text("Configuration:", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w400)),
alignment: AlignmentDirectional.centerStart, const SizedBox(width: 8),
child: Text("Configuration:", style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300)) FutureBuilder(
),
Container(
height: size.height * 0.1,
child: FutureBuilder(
future: getConfigurations(appContext), future: getConfigurations(appContext),
builder: (context, AsyncSnapshot<dynamic> snapshot) { builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data.length > 0) { if (snapshot.data != null && snapshot.data.length > 0) {
return Row( return DropDownConfig(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: DropDownConfig(
configurations: snapshot.data, configurations: snapshot.data,
selectedConfigurationId: inputDevice.configurationId!, selectedConfigurationId: inputDevice.configurationId!,
onChange: (ConfigurationDTO configurationOut) { onChange: (ConfigurationDTO configurationOut) {
inputDevice.configuration = configurationOut.label; inputDevice.configuration = configurationOut.label;
inputDevice.configurationId = configurationOut.id; inputDevice.configurationId = configurationOut.id;
}, },
),
),
],
); );
} else { } else {
return Text(AppLocalizations.of(context)!.noConfigFound); return Text(AppLocalizations.of(context)!.noConfigFound);
} }
} else if (snapshot.connectionState == ConnectionState.none) { } else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data"); return const Text("No data");
} else { } else {
return Center( return const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2));
child: Container(
child: Text("Loading..")
)
);
} }
} }
), ),
),
], ],
), ),
], ],
), ),
Row( const SizedBox(height: 12),
mainAxisAlignment: MainAxisAlignment.spaceAround, // Couleurs + Loader
Wrap(
spacing: 16,
runSpacing: 12,
children: [ children: [
ColorPickerInputContainer( ColorPickerInputContainer(
label: "Couleur principale :", label: "Couleur principale :",
fontSize: 20, fontSize: 16,
color: appConfiguration.primaryColor, color: appConfiguration.primaryColor,
onChanged: (value) { onChanged: (value) => appConfiguration.primaryColor = value,
appConfiguration.primaryColor = value;
},
), ),
ColorPickerInputContainer( ColorPickerInputContainer(
label: AppLocalizations.of(context)!.backgroundColorLabel, label: AppLocalizations.of(context)!.backgroundColorLabel,
fontSize: 20, fontSize: 16,
color: appConfiguration.secondaryColor, color: appConfiguration.secondaryColor,
onChanged: (value) { onChanged: (value) => appConfiguration.secondaryColor = value,
appConfiguration.secondaryColor = value;
},
), ),
ResourceInputContainer( ResourceInputContainer(
label: "Loader :", label: "Loader :",
@ -118,7 +109,7 @@ showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appC
color: kPrimaryColor, color: kPrimaryColor,
imageFit: BoxFit.fitHeight, imageFit: BoxFit.fitHeight,
onChanged: (ResourceDTO resource) async { onChanged: (ResourceDTO resource) async {
if(resource.id == null) { if (resource.id == null) {
appConfiguration.loaderImageId = null; appConfiguration.loaderImageId = null;
appConfiguration.loaderImageUrl = null; appConfiguration.loaderImageUrl = null;
} else { } else {
@ -129,11 +120,14 @@ showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appC
), ),
], ],
), ),
Row( const SizedBox(height: 12),
mainAxisAlignment: MainAxisAlignment.spaceAround, // Pourcentages
Wrap(
spacing: 16,
runSpacing: 12,
children: [ children: [
Container( SizedBox(
height: 100, height: 90,
child: NumberInputContainer( child: NumberInputContainer(
label: "Place des sections (%) :", label: "Place des sections (%) :",
initialValue: appConfiguration.screenPercentageSectionsMainPage ?? 0, initialValue: appConfiguration.screenPercentageSectionsMainPage ?? 0,
@ -143,14 +137,13 @@ showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appC
try { try {
appConfiguration.screenPercentageSectionsMainPage = int.parse(value); appConfiguration.screenPercentageSectionsMainPage = int.parse(value);
} catch (e) { } catch (e) {
print('Screen percentage value 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);
} }
}, },
), ),
), ),
Container( SizedBox(
height: 100, height: 90,
child: NumberInputContainer( child: NumberInputContainer(
label: "Pourcentage des arrondis (0-50) :", label: "Pourcentage des arrondis (0-50) :",
initialValue: appConfiguration.roundedValue ?? 0, initialValue: appConfiguration.roundedValue ?? 0,
@ -160,7 +153,6 @@ showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appC
try { try {
appConfiguration.roundedValue = int.parse(value); appConfiguration.roundedValue = int.parse(value);
} catch (e) { } catch (e) {
print('Rounded value 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);
} }
}, },
@ -168,62 +160,53 @@ showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appC
), ),
], ],
), ),
Row( const SizedBox(height: 12),
mainAxisAlignment: MainAxisAlignment.spaceAround, // Toggles
Wrap(
spacing: 16,
runSpacing: 8,
children: [ children: [
CheckInputContainer( CheckInputContainer(
icon: Icons.image, icon: Icons.image,
label: "Fond pour les images des sections :", label: "Fond pour les images des sections :",
fontSize: 20, fontSize: 16,
isChecked: appConfiguration.isSectionImageBackground, isChecked: appConfiguration.isSectionImageBackground,
onChanged: (value) { onChanged: (value) => appConfiguration.isSectionImageBackground = value,
appConfiguration.isSectionImageBackground = value;
},
), ),
CheckInputContainer( CheckInputContainer(
icon: Icons.watch_later_outlined, icon: Icons.watch_later_outlined,
label: "Heure :", label: "Heure :",
fontSize: 20, fontSize: 16,
isChecked: appConfiguration.isHour, isChecked: appConfiguration.isHour,
onChanged: (value) { onChanged: (value) => appConfiguration.isHour = value,
appConfiguration.isHour = value;
},
), ),
CheckInputContainer( CheckInputContainer(
icon: Icons.date_range, icon: Icons.date_range,
label: "Date :", label: "Date :",
fontSize: 20, fontSize: 16,
isChecked: appConfiguration.isDate, isChecked: appConfiguration.isDate,
onChanged: (value) { onChanged: (value) => appConfiguration.isDate = value,
appConfiguration.isDate = value;
},
), ),
] ],
), ),
], ],
), ),
), ),
actions: <Widget>[ ),
Row( const SizedBox(height: 16),
mainAxisAlignment: MainAxisAlignment.spaceEvenly, Wrap(
spacing: 8,
runSpacing: 8,
alignment: WrapAlignment.end,
children: [ children: [
Container( RoundedButton(
width: 180,
height: 70,
child: RoundedButton(
text: "Annuler", text: "Annuler",
icon: Icons.undo, icon: Icons.undo,
color: kSecond, color: kSecond,
press: () { press: () => Navigator.of(context).pop(),
Navigator.of(context).pop(); fontSize: 16,
},
fontSize: 20,
), ),
), RoundedButton(
Container(
width: 180,
height: 70,
child: RoundedButton(
text: "Changer", text: "Changer",
icon: Icons.check, icon: Icons.check,
color: kPrimaryColor, color: kPrimaryColor,
@ -232,19 +215,21 @@ showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appC
onGetResult(inputDevice, appConfiguration); onGetResult(inputDevice, appConfiguration);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
fontSize: 20, fontSize: 16,
),
), ),
], ],
), ),
], ],
),
),
),
), context: mainContext ), context: mainContext
); );
} }
getConfigurationsElement(DeviceDTO inputDevice, data, Function onGetResult) { getConfigurationsElement(DeviceDTO inputDevice, data, Function onGetResult) {
List<Widget> widgets = <Widget>[]; List<Widget> widgets = <Widget>[];
for(var configuration in data as List<ConfigurationDTO>) { for (var configuration in data as List<ConfigurationDTO>) {
var widget = new InkWell( var widget = new InkWell(
onTap: () { onTap: () {
inputDevice.configuration = configuration.label; inputDevice.configuration = configuration.label;
@ -264,15 +249,8 @@ getConfigurationsElement(DeviceDTO inputDevice, data, Function onGetResult) {
} }
Future<List<ConfigurationDTO>?> getConfigurations(AppContext appContext) async { Future<List<ConfigurationDTO>?> getConfigurations(AppContext appContext) async {
List<ConfigurationDTO>? configurations = await (appContext.getContext() as ManagerAppContext).clientAPI!.configurationApi!.configurationGet(instanceId: (appContext.getContext() as ManagerAppContext).instanceId); return (appContext.getContext() as ManagerAppContext)
//print("number of configurations " + configurations.length.toString()); .clientAPI!
.configurationApi!
if(configurations != null) { .configurationGet(instanceId: (appContext.getContext() as ManagerAppContext).instanceId);
configurations.forEach((element) {
//print(element);
});
}
return configurations;
} }

View File

@ -66,7 +66,7 @@ class _DeviceElementState extends State<DeviceElement> {
children: [ children: [
AutoSizeText( AutoSizeText(
widget.deviceDTO.name != null ? widget.deviceDTO.name! : widget.deviceDTO.identifier!, widget.deviceDTO.name != null ? widget.deviceDTO.name! : widget.deviceDTO.identifier!,
style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w300), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400),
maxLines: 1, maxLines: 1,
), ),
], ],
@ -75,7 +75,7 @@ class _DeviceElementState extends State<DeviceElement> {
Center( Center(
child: Text( child: Text(
updatedDevice?.configuration != null ? updatedDevice?.configuration! ?? "Aucune configuration" : "Aucune configuration", updatedDevice?.configuration != null ? updatedDevice?.configuration! ?? "Aucune configuration" : "Aucune configuration",
style: new TextStyle(fontSize: 20), style: const TextStyle(fontSize: 13),
maxLines: 1, maxLines: 1,
), ),
), ),

View File

@ -60,23 +60,18 @@ class _KioskScreenState extends State<KioskScreen> {
List<AppConfigurationLinkDTO>? appConfigurationLinks = snapshot.data; List<AppConfigurationLinkDTO>? appConfigurationLinks = snapshot.data;
if (snapshot.connectionState == ConnectionState.done && appConfigurationLinks != null) { if (snapshot.connectionState == ConnectionState.done && appConfigurationLinks != null) {
final screenWidth = MediaQuery.of(context).size.width;
final itemWidth = 175;
final crossAxisCount = (screenWidth / itemWidth).floor().clamp(1, 6);
return GridView.builder( return GridView.builder(
shrinkWrap: true, shrinkWrap: true,
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: crossAxisCount), gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 220,
childAspectRatio: 1.5,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: appConfigurationLinks.length, itemCount: appConfigurationLinks.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return Container( return _DeviceCard(
decoration: boxDecoration(), deviceLink: appConfigurationLinks[index],
padding: const EdgeInsets.all(10),
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
child: Align(
alignment: Alignment.center,
child: DeviceElement(deviceDTO: appConfigurationLinks[index].device!, appConfigurationLinkDTO: appConfigurationLinks[index]), //getElement()
),
); );
} }
); );
@ -243,20 +238,47 @@ class _KioskScreenState extends State<KioskScreen> {
}*/ }*/
} }
boxDecoration() { class _DeviceCard extends StatefulWidget {
return BoxDecoration( final AppConfigurationLinkDTO deviceLink;
const _DeviceCard({required this.deviceLink});
@override
State<_DeviceCard> createState() => _DeviceCardState();
}
class _DeviceCardState extends State<_DeviceCard> {
bool _hovered = false;
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: AnimatedScale(
scale: _hovered ? 1.03 : 1.0,
duration: const Duration(milliseconds: 150),
child: Container(
decoration: BoxDecoration(
color: kTextLightColor, color: kTextLightColor,
shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.circular(25.0),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: kSecond, color: kSecond,
spreadRadius: 0.5, spreadRadius: _hovered ? 1 : 0.5,
blurRadius: 5, blurRadius: _hovered ? 10 : 5,
offset: Offset(0, 1.5), // changes position of shadow offset: const Offset(0, 2),
), ),
], ],
),
padding: const EdgeInsets.all(10),
child: DeviceElement(
deviceDTO: widget.deviceLink.device!,
appConfigurationLinkDTO: widget.deviceLink,
),
),
),
); );
}
} }
Future<List<DeviceDTO>?> getDevices(AppContext appContext) async { Future<List<DeviceDTO>?> getDevices(AppContext appContext) async {

View File

@ -14,6 +14,7 @@ import 'package:manager_app/Screens/Kiosk_devices/kiosk_screen.dart';
import 'package:manager_app/Screens/Resources/resources_screen.dart'; import 'package:manager_app/Screens/Resources/resources_screen.dart';
import 'package:manager_app/Screens/Statistics/statistics_screen.dart'; import 'package:manager_app/Screens/Statistics/statistics_screen.dart';
import 'package:manager_app/Screens/Applications/app_configuration_link_screen.dart'; import 'package:manager_app/Screens/Applications/app_configuration_link_screen.dart';
import 'package:manager_app/Screens/Applications/web_app_screen.dart';
import 'package:manager_app/Screens/Notifications/notifications_screen.dart'; import 'package:manager_app/Screens/Notifications/notifications_screen.dart';
import 'package:manager_app/Screens/Users/users_screen.dart'; import 'package:manager_app/Screens/Users/users_screen.dart';
import 'package:manager_app/l10n/app_localizations.dart'; import 'package:manager_app/l10n/app_localizations.dart';
@ -430,6 +431,8 @@ class _MainScreenState extends State<MainScreen> {
child: Column( child: Column(
children: [ children: [
const QuotaBarsWidget(), const QuotaBarsWidget(),
Row(
children: [
DropdownButton<Locale>( DropdownButton<Locale>(
value: managerAppContext.locale, value: managerAppContext.locale,
underline: const SizedBox(), underline: const SizedBox(),
@ -443,12 +446,14 @@ class _MainScreenState extends State<MainScreen> {
if (locale != null) appContext.setLocale(locale); if (locale != null) appContext.setLocale(locale);
}, },
), ),
const SizedBox(height: 4), const SizedBox(width: 8),
AutoSizeText( Expanded(
child: AutoSizeText(
(appContext.getContext() as ManagerAppContext).email ?? "", (appContext.getContext() as ManagerAppContext).email ?? "",
style: TextStyle(color: kBodyTextColor, fontSize: 16, fontWeight: FontWeight.w300, fontFamily: "Helvetica"), style: TextStyle(color: kBodyTextColor, fontSize: 14, fontWeight: FontWeight.w300, fontFamily: "Helvetica"),
maxLines: 1, maxLines: 1,
), ),
),
if ((appContext.getContext() as ManagerAppContext).role == UserRole.SuperAdmin) if ((appContext.getContext() as ManagerAppContext).role == UserRole.SuperAdmin)
IconButton( IconButton(
icon: Icon(Icons.swap_horiz, color: kPrimaryColor), icon: Icon(Icons.swap_horiz, color: kPrimaryColor),
@ -457,8 +462,9 @@ class _MainScreenState extends State<MainScreen> {
), ),
IconButton( IconButton(
icon: Icon(Icons.logout, color: kPrimaryColor), icon: Icon(Icons.logout, color: kPrimaryColor),
tooltip: AppLocalizations.of(context)!.tooltipLogout,
onPressed: () async { onPressed: () async {
var session = await loadJsonSessionFile(); await loadJsonSessionFile();
setState(() { setState(() {
Storage localStorage = window.localStorage; Storage localStorage = window.localStorage;
localStorage.clear(); localStorage.clear();
@ -471,6 +477,8 @@ class _MainScreenState extends State<MainScreen> {
context.go('/login'); context.go('/login');
}); });
}, },
),
],
) )
], ],
), ),
@ -597,25 +605,24 @@ class _MainScreenState extends State<MainScreen> {
switch (elementToShow.type) { switch (elementToShow.type) {
case 'mobile' : case 'mobile' :
var applicationInstanceMobile = instanceDTO.applicationInstanceDTOs!.firstWhere((ai) => ai.appType == AppType.Mobile); var applicationInstanceMobile = instanceDTO.applicationInstanceDTOs?.firstWhereOrNull((ai) => ai.appType == AppType.Mobile);
if (applicationInstanceMobile == null) return const Center(child: Text('Application mobile non configurée'));
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: AppConfigurationLinkScreen(applicationInstanceDTO: applicationInstanceMobile) child: AppConfigurationLinkScreen(applicationInstanceDTO: applicationInstanceMobile)
); );
case 'kiosk' : case 'kiosk' :
var applicationInstanceTablet = instanceDTO.applicationInstanceDTOs!.firstWhere((ai) => ai.appType == AppType.Tablet); var applicationInstanceTablet = instanceDTO.applicationInstanceDTOs?.firstWhereOrNull((ai) => ai.appType == AppType.Tablet);
if (applicationInstanceTablet == null) return const Center(child: Text('Application kiosk non configurée'));
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: KioskScreen(applicationInstanceDTO: applicationInstanceTablet) child: KioskScreen(applicationInstanceDTO: applicationInstanceTablet)
); );
case 'web' : case 'web' :
var applicationInstanceWeb = instanceDTO.applicationInstanceDTOs!.firstWhere((ai) => ai.appType == AppType.Web); return const WebInstanceLoader();
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text("TODO web")
);
case 'vr' : case 'vr' :
var applicationInstanceVR = instanceDTO.applicationInstanceDTOs!.firstWhere((ai) => ai.appType == AppType.VR); var applicationInstanceVR = instanceDTO.applicationInstanceDTOs?.firstWhereOrNull((ai) => ai.appType == AppType.VR);
if (applicationInstanceVR == null) return const Center(child: Text('Application VR non configurée'));
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text("TODO vr") child: Text("TODO vr")

View File

@ -275,7 +275,9 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Row( Wrap(
spacing: 12,
runSpacing: 12,
children: [ children: [
_KpiCard( _KpiCard(
label: l.allNotifications, label: l.allNotifications,
@ -284,7 +286,6 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
selected: _statusFilter == null, selected: _statusFilter == null,
onTap: () => _setFilter(null), onTap: () => _setFilter(null),
), ),
const SizedBox(width: 12),
_KpiCard( _KpiCard(
label: l.sentNotifications, label: l.sentNotifications,
count: _sentCount, count: _sentCount,
@ -292,7 +293,6 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
selected: _statusFilter == 'Sent', selected: _statusFilter == 'Sent',
onTap: () => _setFilter('Sent'), onTap: () => _setFilter('Sent'),
), ),
const SizedBox(width: 12),
_KpiCard( _KpiCard(
label: l.scheduledNotifications, label: l.scheduledNotifications,
count: _scheduledCount, count: _scheduledCount,
@ -300,7 +300,6 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
selected: _statusFilter == 'Scheduled', selected: _statusFilter == 'Scheduled',
onTap: () => _setFilter('Scheduled'), onTap: () => _setFilter('Scheduled'),
), ),
const SizedBox(width: 12),
_KpiCard( _KpiCard(
label: l.failedNotifications, label: l.failedNotifications,
count: _failedCount, count: _failedCount,

View File

@ -13,39 +13,34 @@ import 'package:manager_api_new/api.dart';
dynamic showNewResource(AppContext appContext, BuildContext context) async { dynamic showNewResource(AppContext appContext, BuildContext context) async {
ResourceDTO resourceDetailDTO = new ResourceDTO(); ResourceDTO resourceDetailDTO = new ResourceDTO();
Size size = MediaQuery.of(context).size;
var fileName;
List<File>? filesToSend; List<File>? filesToSend;
List<PlatformFile>? filesToSendWeb; List<PlatformFile>? filesToSendWeb;
var result = await showDialog( var result = await showDialog(
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
content: Container( child: SizedBox(
width: size.width *0.5, width: 560,
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(AppLocalizations.of(context)!.newResource, style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)), Text(
/*Column( AppLocalizations.of(context)!.newResource,
children: [ style: const TextStyle(fontSize: 22, fontWeight: FontWeight.w400),
StringInputContainer(
label: "Nom :",
initialValue: resourceDetailDTO.label,
onChanged: (value) {
resourceDetailDTO.label = value;
},
), ),
if (fileName != null) new Text(fileName), const SizedBox(height: 10),
], SizedBox(
),*/ height: 400,
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Container(
width: size.width *0.85,
height: size.height *0.5,
child: ResourceTab( child: ResourceTab(
resourceDTO: resourceDetailDTO, resourceDTO: resourceDetailDTO,
onFileUpload: (List<File> files) { onFileUpload: (List<File> files) {
@ -54,71 +49,53 @@ dynamic showNewResource(AppContext appContext, BuildContext context) async {
onFileUploadWeb: (List<PlatformFile> files) { onFileUploadWeb: (List<PlatformFile> files) {
filesToSendWeb = files; filesToSendWeb = files;
}, },
)
), ),
), ),
], ],
), ),
), ),
), ),
actions: <Widget>[ const SizedBox(height: 16),
Row( Wrap(
mainAxisAlignment: MainAxisAlignment.spaceAround, spacing: 8,
runSpacing: 8,
alignment: WrapAlignment.end,
children: [ children: [
Align( RoundedButton(
alignment: AlignmentDirectional.bottomEnd,
child: Container(
width: 175,
height: 70,
child: RoundedButton(
text: AppLocalizations.of(context)!.cancel, text: AppLocalizations.of(context)!.cancel,
icon: Icons.undo, icon: Icons.undo,
color: kSecond, color: kSecond,
press: () { press: () => Navigator.of(context).pop(),
Navigator.of(context).pop(); fontSize: 16,
},
fontSize: 20,
), ),
), RoundedButton(
),
Align(
alignment: AlignmentDirectional.bottomEnd,
child: Container(
width: 150,
height: 70,
child: RoundedButton(
text: AppLocalizations.of(context)!.create, text: AppLocalizations.of(context)!.create,
icon: Icons.check, icon: Icons.check,
color: kPrimaryColor, color: kPrimaryColor,
textColor: kWhite, textColor: kWhite,
press: () { press: () {
//if (resourceDetailDTO.label != null && resourceDetailDTO.label!.trim() != '') { if (kIsWeb) {
if(kIsWeb) { if (resourceDetailDTO.url != null || filesToSendWeb != null) {
if(resourceDetailDTO.url != null || filesToSendWeb != null) { // TODO clarify resourceDetailDTO.data != null
Navigator.pop(context, [resourceDetailDTO, filesToSend, filesToSendWeb]); Navigator.pop(context, [resourceDetailDTO, filesToSend, filesToSendWeb]);
} else { } else {
showNotification(Colors.orange, kWhite, AppLocalizations.of(context)!.resourceNoFileLoaded, context, null); showNotification(Colors.orange, kWhite, AppLocalizations.of(context)!.resourceNoFileLoaded, context, null);
} }
} else { } else {
if (resourceDetailDTO.url != null || filesToSendWeb!.length > 0 || filesToSend!.length > 0) { // TODO clarify resourceDetailDTO.data != null if (resourceDetailDTO.url != null || filesToSendWeb!.length > 0 || filesToSend!.length > 0) {
Navigator.pop(context, [resourceDetailDTO, filesToSend, filesToSendWeb]); Navigator.pop(context, [resourceDetailDTO, filesToSend, filesToSendWeb]);
} else { } else {
showNotification(Colors.orange, kWhite, AppLocalizations.of(context)!.resourceNoFileLoaded, context, null); showNotification(Colors.orange, kWhite, AppLocalizations.of(context)!.resourceNoFileLoaded, context, null);
} }
} }
/*} else {
showNotification(Colors.orange, kWhite, 'Veuillez donner un nom à la ressource', context, null);
}*/
//Navigator.of(context).pop();
//create(resourceDetailDTO, fileToSend, appContext, context);
}, },
fontSize: 20, fontSize: 16,
),
),
), ),
], ],
), ),
], ],
),
),
),
), context: context ), context: context
); );

View File

@ -11,7 +11,7 @@ import 'package:provider/provider.dart';
import 'package:diacritic/diacritic.dart'; import 'package:diacritic/diacritic.dart';
class ResourceBodyGrid extends StatefulWidget { class ResourceBodyGrid extends StatefulWidget {
final List<ResourceDTO> resources; //return ResourceDTO final List<ResourceDTO> resources;
final Function onSelect; final Function onSelect;
final bool isAddButton; final bool isAddButton;
final bool isSelectModal; final bool isSelectModal;
@ -22,7 +22,7 @@ class ResourceBodyGrid extends StatefulWidget {
required this.onSelect, required this.onSelect,
required this.isAddButton, required this.isAddButton,
required this.resourceTypesIn, required this.resourceTypesIn,
this.isSelectModal = false this.isSelectModal = false,
}) : super(key: key); }) : super(key: key);
@override @override
@ -39,34 +39,42 @@ class _ResourceBodyGridState extends State<ResourceBodyGrid> {
@override @override
void initState() { void initState() {
resourcesToShow = widget.resources; resourcesToShow = widget.resources;
currentFilterTypes = resource_types.where((rt) => widget.resourceTypesIn.contains(rt.type)).map((rt) => rt.label).toList();//, resource_types[2]]; // resource_types[3] currentFilterTypes = resource_types
filterTypes = resource_types.where((rt) => widget.resourceTypesIn.contains(rt.type)).map((rt) => rt.label).toList();//, resource_types[2]]; // resource_types[3] .where((rt) => widget.resourceTypesIn.contains(rt.type))
selectedTypes = resource_types.where((rt) => widget.resourceTypesIn.contains(rt.type)).map((rt) => rt.label).toList(); .map((rt) => rt.label)
.toList();
filterTypes = resource_types
.where((rt) => widget.resourceTypesIn.contains(rt.type))
.map((rt) => rt.label)
.toList();
selectedTypes = resource_types
.where((rt) => widget.resourceTypesIn.contains(rt.type))
.map((rt) => rt.label)
.toList();
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context); final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size; return bodyGrid(resourcesToShow, appContext);
return bodyGrid(resourcesToShow, size, appContext);
} }
Widget bodyGrid(data, Size size, AppContext appContext) { Widget bodyGrid(List<ResourceDTO> data, AppContext appContext) {
final screenWidth = MediaQuery.of(context).size.width; final canEdit = (appContext.getContext() as ManagerAppContext).canEdit;
final itemWidth = 150;
final crossAxisCount = (screenWidth / itemWidth).floor().clamp(1, 9);
return Column( return Column(
children: [ children: [
Row( // Header: search + filter + add button Wrap pour mobile
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.fromLTRB(8, 8, 8, 4),
child: StringInputContainer( child: Wrap(
label: "Recherche:", spacing: 8,
runSpacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
StringInputContainer(
label: 'Recherche:',
onChanged: (String value) { onChanged: (String value) {
setState(() { setState(() {
filterSearch = value; filterSearch = value;
@ -74,156 +82,214 @@ class _ResourceBodyGridState extends State<ResourceBodyGrid> {
}); });
}, },
), ),
), MultiSelectContainer(
Padding( label: 'Type:',
padding: const EdgeInsets.all(8.0),
child: MultiSelectContainer(
label: "Type:",
initialValue: filterTypes, initialValue: filterTypes,
values: currentFilterTypes, values: currentFilterTypes,
isMultiple: true, isMultiple: true,
onChanged: (result) { onChanged: (result) {
setState(() { setState(() {
selectedTypes = result as List<String>; // TO TEST selectedTypes = result as List<String>;
filterResource(); filterResource();
}); });
}, },
), ),
), if (widget.isAddButton && canEdit)
if (widget.isAddButton && (appContext.getContext() as ManagerAppContext).canEdit) _AddButton(onTap: () {
InkWell( widget.onSelect(
onTap: () { ResourceDTO(id: widget.isSelectModal ? '-1' : null));
widget.onSelect(ResourceDTO(id: widget.isSelectModal ? "-1" : null));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: size.height *0.08,
width: size.height *0.08,
decoration: BoxDecoration(
color: kSuccess,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(25.0),
boxShadow: [
BoxShadow(
color: kSecond,
spreadRadius: 0.5,
blurRadius: 5,
offset: Offset(0, 1.5), // changes position of shadow
),
],
),
child:
Padding(
padding: const EdgeInsets.all(10.0),
child: LayoutBuilder(builder: (context, constraint) {
return new Icon(Icons.add, size: constraint.biggest.height, color: kTextLightColor);
}), }),
),
),
),
)
], ],
), ),
),
// Grid
Expanded( Expanded(
child: GridView.builder( child: GridView.builder(
shrinkWrap: true, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: crossAxisCount), gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 160,
childAspectRatio: 1.0,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: data.length, itemCount: data.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return return _ResourceCard(
InkWell( resource: resourcesToShow[index],
onTap: () { onTap: () => widget.onSelect(resourcesToShow[index]),
widget.onSelect(resourcesToShow[index]); );
}, },
child: Container(
decoration: boxDecoration(data[index], appContext),
padding: const EdgeInsets.all(15),
margin: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
child: Align(
alignment: Alignment.center,
child: getElement(data[index], size, appContext)
),
),
);
}
), ),
), ),
], ],
); );
} }
getElement(ResourceDTO resource, Size size, AppContext appContext) {
if (resource.id != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: size.width *0.01,
),
Align(
alignment: Alignment.center,
child: AutoSizeText(
resource.label == null ? "" : resource.label!,
style: new TextStyle(fontSize: 20),
maxLines: 1,
),
),
Align(
alignment: Alignment.bottomRight,
child: Icon(
getResourceIcon(resource.type),
color: kPrimaryColor,
size: 15,
),
),
],
);
} else {
return Icon(
Icons.close,
color: kTextLightColor,
size: 50.0,
);
}
}
void filterResource() { void filterResource() {
resourcesToShow = filterSearch.isEmpty ? widget.resources: widget.resources.where((ResourceDTO resource) => resource.id == null || removeDiacritics(resource.label!.toUpperCase()).contains(removeDiacritics(filterSearch.toUpperCase()))).toList(); resourcesToShow = filterSearch.isEmpty
var getTypesInSelected = resource_types.where((ft) => selectedTypes.contains(ft.label)).map((rt) => rt.type).toList(); ? widget.resources
resourcesToShow = resourcesToShow.where((resource) => resource.id == null || getTypesInSelected.contains(resource.type)).toList(); : widget.resources
.where((ResourceDTO resource) =>
resource.id == null ||
removeDiacritics(resource.label!.toUpperCase())
.contains(removeDiacritics(filterSearch.toUpperCase())))
.toList();
var getTypesInSelected = resource_types
.where((ft) => selectedTypes.contains(ft.label))
.map((rt) => rt.type)
.toList();
resourcesToShow = resourcesToShow
.where((resource) =>
resource.id == null ||
getTypesInSelected.contains(resource.type))
.toList();
} }
} }
boxDecoration(dynamic resourceDetailDTO, appContext) { // Add button
return BoxDecoration(
color: resourceDetailDTO.id == null ? kSecond : kBackgroundColor, class _AddButton extends StatelessWidget {
shape: BoxShape.rectangle, final VoidCallback onTap;
borderRadius: BorderRadius.circular(30.0), const _AddButton({required this.onTap});
image: resourceDetailDTO.id != null && (resourceDetailDTO.type == ResourceType.Image || resourceDetailDTO.type == ResourceType.ImageUrl) && resourceDetailDTO.url != null ? new DecorationImage(
fit: BoxFit.cover, @override
colorFilter: new ColorFilter.mode(Colors.black.withValues(alpha: 0.3), BlendMode.dstATop), Widget build(BuildContext context) {
image: new NetworkImage( return GestureDetector(
resourceDetailDTO.url!, onTap: onTap,
child: Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: kSuccess,
borderRadius: BorderRadius.circular(16),
boxShadow: const [kDefaultShadow],
), ),
) : null, child: const Icon(Icons.add, color: kTextLightColor, size: 28),
),
);
}
}
// Resource card
class _ResourceCard extends StatefulWidget {
final ResourceDTO resource;
final VoidCallback onTap;
const _ResourceCard({required this.resource, required this.onTap});
@override
State<_ResourceCard> createState() => _ResourceCardState();
}
class _ResourceCardState extends State<_ResourceCard> {
bool _hovered = false;
@override
Widget build(BuildContext context) {
final resource = widget.resource;
final isRemove = resource.id == null;
final hasImage = !isRemove &&
(resource.type == ResourceType.Image ||
resource.type == ResourceType.ImageUrl) &&
resource.url != null;
return MouseRegion(
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: GestureDetector(
onTap: widget.onTap,
child: AnimatedScale(
scale: _hovered ? 1.03 : 1.0,
duration: const Duration(milliseconds: 150),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: kSecond, color: kSecond,
spreadRadius: 0.5, spreadRadius: _hovered ? 1 : 0.5,
blurRadius: 5, blurRadius: _hovered ? 10 : 5,
offset: Offset(0, 1.5), // changes position of shadow offset: const Offset(0, 2),
), ),
], ],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Stack(
fit: StackFit.expand,
children: [
// Background
if (hasImage)
Image.network(resource.url!, fit: BoxFit.cover)
else
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: isRemove
? [kSecond, kSecond.withValues(alpha: 0.7)]
: [kPrimaryColor, kPrimaryColor.withValues(alpha: 0.65)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
// Gradient scrim (only when there's an image)
if (hasImage)
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: const [0.35, 1.0],
colors: [
Colors.transparent,
Colors.black.withValues(alpha: 0.65),
],
),
),
),
// Type icon top right badge
if (!isRemove)
Positioned(
top: 8,
right: 8,
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(6),
),
child: Icon(
getResourceIcon(resource.type),
color: kTextLightColor,
size: 14,
),
),
),
// Label bottom left
if (!isRemove)
Positioned(
left: 8,
right: 8,
bottom: 8,
child: AutoSizeText(
resource.label ?? '',
style: kCardTitleStyle.copyWith(fontSize: 13),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
// Remove icon
if (isRemove)
const Center(
child: Icon(Icons.close, color: kTextLightColor, size: 40),
),
],
),
),
),
),
),
); );
}
/*Future<List<ResourceDTO>> getResources(Function onGetResult, bool isImage, AppContext appContext) async {
List<ResourceDTO> resources = await (appContext.getContext() as ManagerAppContext).clientAPI.resourceApi.resourceGet(instanceId:(appContext.getContext() as ManagerAppContext).instanceId);
if (onGetResult != null && isImage) {
resources = resources.where((element) => element.type == ResourceType.image || element.type == ResourceType.imageUrl).toList();
} }
return resources; }
}*/

View File

@ -15,20 +15,25 @@ import 'get_element_for_resource.dart';
Future<ResourceDTO?> showResource(ResourceDTO resourceDTO, AppContext appContext, BuildContext context, Size size) async { Future<ResourceDTO?> showResource(ResourceDTO resourceDTO, AppContext appContext, BuildContext context, Size size) async {
var result = await showDialog( var result = await showDialog(
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)) borderRadius: BorderRadius.all(Radius.circular(20.0))
), ),
content: SingleChildScrollView(
child: Column(
children: [
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox( child: SizedBox(
width: MediaQuery.of(context).size.width * 0.6, width: 520,
height: MediaQuery.of(context).size.height * 0.2, child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 80,
child: StringInputContainer( child: StringInputContainer(
label: "Label :", label: "Label :",
initialValue: resourceDTO.label != null ? resourceDTO.label : "", initialValue: resourceDTO.label != null ? resourceDTO.label : "",
@ -36,19 +41,17 @@ Future<ResourceDTO?> showResource(ResourceDTO resourceDTO, AppContext appContext
resourceDTO.label = value; resourceDTO.label = value;
}, },
), ),
),/*Text(
resourceDTO.label == null ? "" : resourceDTO.label,
style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)),*/
), ),
const SizedBox(height: 8),
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: resourceDTO.type == ResourceType.Audio ? 80 : 300,
maxWidth: resourceDTO.type == ResourceType.Audio ? 200 : double.infinity,
), ),
Container(
height: resourceDTO.type == ResourceType.Audio ? size.height *0.1 : size.height *0.3,
width: resourceDTO.type == ResourceType.Audio ? size.width *0.1 : size.width *0.3,
child: Center(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
border: Border.all(width: 3, color: kSecond) border: Border.all(width: 3, color: kSecond),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
@ -56,85 +59,46 @@ Future<ResourceDTO?> showResource(ResourceDTO resourceDTO, AppContext appContext
), ),
), ),
), ),
),
], ],
), ),
), ),
actions: <Widget>[ ),
Row( const SizedBox(height: 16),
mainAxisAlignment: MainAxisAlignment.spaceAround, Wrap(
spacing: 8,
runSpacing: 8,
alignment: WrapAlignment.end,
children: [ children: [
Padding( RoundedButton(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: AlignmentDirectional.bottomEnd,
child: Container(
width: 175,
height: 70,
child: RoundedButton(
text: "Retour", text: "Retour",
icon: Icons.undo, icon: Icons.undo,
color: kSecond, color: kSecond,
press: () { press: () => Navigator.of(context).pop(),
Navigator.of(context).pop(); fontSize: 16,
},
fontSize: 20,
), ),
), RoundedButton(
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: AlignmentDirectional.bottomEnd,
child: Container(
width: 200,
height: 70,
child: RoundedButton(
text: "Supprimer", text: "Supprimer",
icon: Icons.delete, icon: Icons.delete,
color: kError, color: kError,
textColor: kWhite, textColor: kWhite,
press: () { press: () => delete(resourceDTO, appContext, context),
delete(resourceDTO, appContext, context); fontSize: 16,
},
fontSize: 20,
), ),
), RoundedButton(
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: AlignmentDirectional.bottomEnd,
child: Container(
width: 220,
height: 70,
child: RoundedButton(
text: AppLocalizations.of(context)!.download, text: AppLocalizations.of(context)!.download,
icon: Icons.download, icon: Icons.download,
color: kPrimaryColor, color: kPrimaryColor,
press: () { press: () {
if(resourceDTO.url != null) { if (resourceDTO.url != null) {
html.AnchorElement anchorElement = html.AnchorElement(href: resourceDTO.url!); html.AnchorElement anchorElement = html.AnchorElement(href: resourceDTO.url!);
anchorElement.download = '${resourceDTO.label}.json'; anchorElement.download = '${resourceDTO.label}.json';
anchorElement.target = '_blank'; anchorElement.target = '_blank';
anchorElement.click(); anchorElement.click();
} }
}, },
fontSize: 20, fontSize: 16,
), ),
), RoundedButton(
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: AlignmentDirectional.bottomEnd,
child: Container(
width: 220,
height: 70,
child: RoundedButton(
text: AppLocalizations.of(context)!.save, text: AppLocalizations.of(context)!.save,
icon: Icons.check, icon: Icons.check,
color: kPrimaryColor, color: kPrimaryColor,
@ -143,14 +107,14 @@ Future<ResourceDTO?> showResource(ResourceDTO resourceDTO, AppContext appContext
Navigator.pop(context, resourceDTO); Navigator.pop(context, resourceDTO);
} }
}, },
fontSize: 20, fontSize: 16,
),
),
),
), ),
], ],
), ),
], ],
),
),
),
), context: context ), context: context
); );
return result; return result;
@ -162,11 +126,9 @@ Future<void> delete(ResourceDTO resourceDTO, AppContext appContext, context) asy
() {}, () {},
() async { () async {
await (appContext.getContext() as ManagerAppContext).clientAPI!.resourceApi!.resourceDelete(resourceDTO.id!); await (appContext.getContext() as ManagerAppContext).clientAPI!.resourceApi!.resourceDelete(resourceDTO.id!);
// TODO Remove from firebase storage via service or ?
FirebaseStorage storage = FirebaseStorage.instance; FirebaseStorage storage = FirebaseStorage.instance;
Reference storageReference = storage.ref().child('pictures/${appContext.getContext().instanceId}/${resourceDTO.id}'); Reference storageReference = storage.ref().child('pictures/${appContext.getContext().instanceId}/${resourceDTO.id}');
// Supprimer le fichier
try { try {
await storageReference.delete(); await storageReference.delete();
print('Fichier supprimé avec succès'); print('Fichier supprimé avec succès');
@ -176,7 +138,6 @@ Future<void> delete(ResourceDTO resourceDTO, AppContext appContext, context) asy
try {} catch (e) {} try {} catch (e) {}
// just to refresh
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
appContext.setContext(managerAppContext); appContext.setContext(managerAppContext);
Navigator.of(context).pop(); Navigator.of(context).pop();

View File

@ -16,6 +16,34 @@ const kWhite = Color(0xFFFFFFFF);
const kBlack = Color(0xFF000000); const kBlack = Color(0xFF000000);
const kSuccess = Color(0xFF8bc34a); const kSuccess = Color(0xFF8bc34a);
// Responsive
const kBreakpointMobile = 850.0;
// Shadows
const kDefaultShadow = BoxShadow(
color: kSecond,
spreadRadius: 0.5,
blurRadius: 5,
offset: Offset(0, 1.5),
);
// TextStyles for cards
const kCardTitleStyle = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: kWhite,
);
const kCardSubtitleStyle = TextStyle(
fontSize: 11,
fontWeight: FontWeight.w300,
color: Color(0xCCFFFFFF),
);
const kSectionLabelStyle = TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: kTitleTextColor,
);
const List<String> section_types = ["Map", "Slider", "Video", "Web", "Menu", "Quiz", "Article", "PDF", "Game", "Agenda", "Weather", "Event"]; const List<String> section_types = ["Map", "Slider", "Video", "Web", "Menu", "Quiz", "Article", "PDF", "Game", "Agenda", "Weather", "Event"];
const List<String> map_types = ["none", "normal", "satellite", "terrain", "hybrid"]; const List<String> map_types = ["none", "normal", "satellite", "terrain", "hybrid"];
const List<String> languages = ["FR", "NL", "EN", "DE", "IT", "ES", "CN", "PL", "AR", "UK"]; const List<String> languages = ["FR", "NL", "EN", "DE", "IT", "ES", "CN", "PL", "AR", "UK"];

View File

@ -52,6 +52,7 @@
"noInstanceFound": "No instance found", "noInstanceFound": "No instance found",
"configurePlan": "Configure plan", "configurePlan": "Configure plan",
"tooltipSwitchInstance": "Switch instance", "tooltipSwitchInstance": "Switch instance",
"tooltipLogout": "Log out",
"usersTitle": "Users", "usersTitle": "Users",
"createUserBtn": "Create user", "createUserBtn": "Create user",

View File

@ -52,6 +52,7 @@
"noInstanceFound": "Aucune instance trouvée", "noInstanceFound": "Aucune instance trouvée",
"configurePlan": "Configurer le plan", "configurePlan": "Configurer le plan",
"tooltipSwitchInstance": "Changer d'instance", "tooltipSwitchInstance": "Changer d'instance",
"tooltipLogout": "Se déconnecter",
"usersTitle": "Utilisateurs", "usersTitle": "Utilisateurs",
"createUserBtn": "Créer un utilisateur", "createUserBtn": "Créer un utilisateur",
@ -369,7 +370,7 @@
"configRemoveConfirm": "Êtes-vous sûr de vouloir retirer cette configuration de l'application ?", "configRemoveConfirm": "Êtes-vous sûr de vouloir retirer cette configuration de l'application ?",
"configRemovedSuccess": "La configuration a été retirée de l'application avec succès", "configRemovedSuccess": "La configuration a été retirée de l'application avec succès",
"configRemoveError": "Une erreur est survenue lors du retrait de la configuration", "configRemoveError": "Une erreur est survenue lors du retrait de la configuration",
"phoneConfigTitle": "Configurations par appareil", "phoneConfigTitle": "Contenu de l'application mobile",
"addConfig": "Ajouter une configuration", "addConfig": "Ajouter une configuration",
"selectConfigToAdd": "Sélectionner une configuration à ajouter", "selectConfigToAdd": "Sélectionner une configuration à ajouter",
"noItems": "Aucun élément à afficher", "noItems": "Aucun élément à afficher",

View File

@ -328,6 +328,12 @@ abstract class AppLocalizations {
/// **'Changer d\'instance'** /// **'Changer d\'instance'**
String get tooltipSwitchInstance; String get tooltipSwitchInstance;
/// No description provided for @tooltipLogout.
///
/// In fr, this message translates to:
/// **'Se déconnecter'**
String get tooltipLogout;
/// No description provided for @usersTitle. /// No description provided for @usersTitle.
/// ///
/// In fr, this message translates to: /// In fr, this message translates to:
@ -1909,7 +1915,7 @@ abstract class AppLocalizations {
/// No description provided for @phoneConfigTitle. /// No description provided for @phoneConfigTitle.
/// ///
/// In fr, this message translates to: /// In fr, this message translates to:
/// **'Configurations par appareil'** /// **'Contenu de l\'application mobile'**
String get phoneConfigTitle; String get phoneConfigTitle;
/// No description provided for @addConfig. /// No description provided for @addConfig.

View File

@ -126,6 +126,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get tooltipSwitchInstance => 'Switch instance'; String get tooltipSwitchInstance => 'Switch instance';
@override
String get tooltipLogout => 'Log out';
@override @override
String get usersTitle => 'Users'; String get usersTitle => 'Users';

View File

@ -127,6 +127,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override @override
String get tooltipSwitchInstance => 'Changer d\'instance'; String get tooltipSwitchInstance => 'Changer d\'instance';
@override
String get tooltipLogout => 'Se déconnecter';
@override @override
String get usersTitle => 'Utilisateurs'; String get usersTitle => 'Utilisateurs';
@ -998,7 +1001,7 @@ class AppLocalizationsFr extends AppLocalizations {
'Une erreur est survenue lors du retrait de la configuration'; 'Une erreur est survenue lors du retrait de la configuration';
@override @override
String get phoneConfigTitle => 'Configurations par appareil'; String get phoneConfigTitle => 'Contenu de l\'application mobile';
@override @override
String get addConfig => 'Ajouter une configuration'; String get addConfig => 'Ajouter une configuration';

View File

@ -127,6 +127,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override @override
String get tooltipSwitchInstance => 'Instantie wisselen'; String get tooltipSwitchInstance => 'Instantie wisselen';
@override
String get tooltipLogout => 'Uitloggen';
@override @override
String get usersTitle => 'Gebruikers'; String get usersTitle => 'Gebruikers';

View File

@ -52,6 +52,7 @@
"noInstanceFound": "Geen instantie gevonden", "noInstanceFound": "Geen instantie gevonden",
"configurePlan": "Plan configureren", "configurePlan": "Plan configureren",
"tooltipSwitchInstance": "Instantie wisselen", "tooltipSwitchInstance": "Instantie wisselen",
"tooltipLogout": "Uitloggen",
"usersTitle": "Gebruikers", "usersTitle": "Gebruikers",
"createUserBtn": "Gebruiker aanmaken", "createUserBtn": "Gebruiker aanmaken",

View File

@ -56,6 +56,7 @@ Future<void> main() async {
locale: const Locale('fr'), locale: const Locale('fr'),
supportedLocales: const [Locale('fr')], supportedLocales: const [Locale('fr')],
localizationsDelegates: const [ localizationsDelegates: const [
AppLocalizations.delegate,
FlutterQuillLocalizations.delegate, FlutterQuillLocalizations.delegate,
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, GlobalWidgetsLocalizations.delegate,

View File

@ -34,6 +34,8 @@ class InstanceDTO {
this.statsHistoryDays, this.statsHistoryDays,
this.hasAdvancedStats, this.hasAdvancedStats,
this.applicationInstanceDTOs = const [], this.applicationInstanceDTOs = const [],
this.webSlug,
this.publicApiKey,
}); });
String? id; String? id;
@ -114,6 +116,10 @@ class InstanceDTO {
List<ApplicationInstanceDTO>? applicationInstanceDTOs; List<ApplicationInstanceDTO>? applicationInstanceDTOs;
String? webSlug;
String? publicApiKey;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
@ -272,6 +278,16 @@ class InstanceDTO {
} else { } else {
json[r'applicationInstanceDTOs'] = null; json[r'applicationInstanceDTOs'] = null;
} }
if (this.webSlug != null) {
json[r'webSlug'] = this.webSlug;
} else {
json[r'webSlug'] = null;
}
if (this.publicApiKey != null) {
json[r'publicApiKey'] = this.publicApiKey;
} else {
json[r'publicApiKey'] = null;
}
return json; return json;
} }
@ -318,6 +334,8 @@ class InstanceDTO {
hasAdvancedStats: mapValueOfType<bool>(json, r'hasAdvancedStats'), hasAdvancedStats: mapValueOfType<bool>(json, r'hasAdvancedStats'),
applicationInstanceDTOs: ApplicationInstanceDTO.listFromJson( applicationInstanceDTOs: ApplicationInstanceDTO.listFromJson(
json[r'applicationInstanceDTOs']), json[r'applicationInstanceDTOs']),
webSlug: mapValueOfType<String>(json, r'webSlug'),
publicApiKey: mapValueOfType<String>(json, r'publicApiKey'),
); );
} }
return null; return null;

70
web/favicon.svg Normal file
View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
width="439"
height="419"
id="svg137"
sodipodi:docname="MyInfoMate_logo_only.svg"
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs137" />
<sodipodi:namedview
id="namedview137"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="5.6568543"
inkscape:cx="348.69203"
inkscape:cy="38.448931"
inkscape:window-width="5120"
inkscape:window-height="1369"
inkscape:window-x="1912"
inkscape:window-y="1072"
inkscape:window-maximized="1"
inkscape:current-layer="svg137" />
<path
d="m 178.04447,135.21247 c 0.002,9.09742 0.17625,49.4843 0.17178,58.58172 -0.005,10.44131 -0.004,20.8826 0.002,31.32392 0.005,9.06176 0.005,18.12351 0.003,27.18527 -0.002,5.37036 -0.002,10.7407 0.002,16.11105 0.0204,38.14483 0.0204,38.14483 -0.54694,53.99548 l -0.10305,3.01772 c -0.80885,18.6575 -7.8096,35.94603 -20.4863,49.70518 l -2.15235,2.35938 c -12.39998,12.98838 -30.69723,23.86033 -49.02594,24.74586 -2.64076,0.0575 -5.28022,0.0839 -7.921566,0.10008 l -3.044189,0.03 c -3.328264,0.0316 -6.656542,0.0564 -9.984864,0.0811 -2.346596,0.0206 -4.693189,0.0416 -7.039779,0.063 -4.940416,0.044 -9.880843,0.0849 -14.821305,0.12331 -6.216775,0.0484 -12.433489,0.10212 -18.650202,0.158 -6.037887,0.054 -17.776846,0.19345 -40.2740759,0.28883 L 57.571753,137.10368 c 14.685204,-1.44088 35.90966,0.0974 45.537897,0.12284 41.79401,0.11064 74.93393,-6.04872 74.93482,-2.01405 z"
fill="#d62f60"
id="path1"
transform="translate(249.91406,6.8671875)"
sodipodi:nodetypes="cscsccccccccccscccsc" />
<path
d="m 64.688834,203.13197 c 0.0035,6.66536 0.0067,13.33073 0.0093,19.99609 l 9.86e-4,2.48865 c 0.005,13.72121 -0.0058,27.44236 -0.02756,41.16355 -0.01265,8.44313 -0.01133,16.88604 0.0085,25.32916 0.01266,5.78741 0.0111,11.57471 -0.0014,17.36211 -0.0067,3.33159 -0.0047,6.66256 0.01058,9.99416 0.111194,25.65838 0.111194,25.65838 -4.500365,37.04128 l -0.849251,2.18899 c -7.771476,18.55174 -21.788527,30.7335 -39.685905,38.97898 -3.37688,1.34444 -5.301227,2.48389 -8.978708,2.48389 l -3.2442484,1.47057 c -4.8987376,0.0599 -14.0045433,0.089 -15.4540455,0.10655 -25.7222011,0.30356 -49.7229121,-5.03616 -68.8916011,-23.19214 -3.286346,-3.24956 -6.197141,-6.66938 -8.810303,-10.47827 -1.170385,-1.6798 -3.844026,-5.57044 -5.590218,-8.45711 -0.851523,-1.40767 -3.595298,-4.4516 -7.983006,-16.21404 -0.853709,-1.50963 -2.403339,-12.94213 -2.403339,-12.94213 -0.0915,-1.48718 -0.23669,-2.92345 -0.23801,-4.41344 l -0.01,-2.78866 0.005,-3.06828 -0.007,-3.25582 c -0.006,-3.61823 -0.006,-7.23642 -0.005,-10.85466 -0.003,-2.58667 -0.006,-5.17335 -0.0102,-7.76002 -0.008,-6.30723 -0.0111,-12.61445 -0.0116,-18.92169 -4.7e-4,-5.12481 -0.003,-10.24962 -0.006,-15.37442 -0.009,-14.52085 -0.0134,-29.0417 -0.0127,-43.56255 l 1.3e-4,-2.37302 1.2e-4,-2.3759 c 4.1e-4,-12.71422 -0.009,-25.42841 -0.0233,-38.14262 -0.0143,-13.04611 -0.0213,-26.0922 -0.0204,-39.13831 3.3e-4,-7.32798 -0.002,-14.65593 -0.0132,-21.9839 -0.01,-6.88802 -0.01,-13.77597 -0.002,-20.663998 10e-4,-2.533683 1.124,-6.567371 1.118,-9.101047 -0.008,-3.44738 -1.128,-5.394528 82.246422,49.699935 83.374422,55.09446 83.377322,60.92629 83.380322,66.75811 z"
fill="#51cdc8"
id="path2"
transform="translate(112.81113,8.4930303)"
sodipodi:nodetypes="sscccccccccccccsccccccccscsccccccccs" />
<path
d="m 0,0 c 2.3047995,-6.7559e-4 4.6095989,-0.00164773 6.9143982,-0.00289917 4.8125578,-0.00146675 9.6250968,6.4331e-4 14.4376528,0.00534058 6.108101,0.00567724 12.216159,0.00242443 18.324259,-0.00357247 4.756666,-0.00364996 9.513323,-0.00244655 14.269991,1.2779e-4 2.249005,6.5808e-4 4.498012,-1.1905e-4 6.747016,-0.00247955 10.980782,-0.00899429 21.945816,0.06768592 32.917523,0.54693985 l 2.536386,0.10305596 C 119.97909,1.8334817 140.8053,14.193522 157.08594,31.132812 c 14.55199,16.47268 20.55085,36.163532 20.75,57.8125 0.0272,1.535344 0.0557,3.070663 0.0855,4.605958 0.0684,3.811023 0.11277,7.62181 0.14563,11.43329 0.018,2.0392 0.0412,21.15431 0.0616,30.38266 0.0376,3.46864 -119.042723,1.76559 -119.042723,1.76559 l -20.85965,1.44008 -111.140349,85.55992 c -0.161213,-39.22828 -0.161213,-39.22828 -0.195313,-55.73828 -0.02368,-11.37426 -0.05144,-22.7484 -0.106445,-34.12256 -0.04005,-8.28583 -0.06572,-16.57157 -0.0746,-24.8575 -0.0052,-4.3814 -0.01724,-8.76252 -0.04657,-13.143825 -0.02746,-4.136862 -0.03546,-8.273336 -0.02951,-12.41028 -0.0011,-1.50588 -0.0089,-3.011774 -0.02439,-4.517574 -0.131739,-13.445652 1.806738,-26.659985 8.289322,-38.647479 l 1.049072,-2.008056 c 2.851223,-5.317287 6.068313,-10.084275 10.138428,-14.554444 L -51.898437,21.875 C -43.655062,13.127533 -37.90534,5.8098811 -26.601562,1.8203125 l -12.3125,0.3125 v -1 C -25.942811,-0.00501656 -13.012554,-0.01357216 0,0 Z"
fill="#fec728"
id="path3"
transform="translate(249.91406,6.8671875)"
sodipodi:nodetypes="cccccccccccccccccccccccccccccc" />
<path
d="M 122.4545,-1.5555303 C 104.75322,5.7344897 77.764999,26.892615 70.396697,44.304966 65.927561,56.152831 64.595666,67.366945 64.64297,79.919079 c -0.0088,1.568853 -0.01926,3.137697 -0.03133,4.706528 -0.02754,4.203537 -0.03127,8.406777 -0.03028,12.610391 -0.0032,4.413592 -0.02883,8.827052 -0.05198,13.240572 -0.04035,8.33285 -0.06085,16.66561 -0.07397,24.99854 -0.01594,9.49723 -0.05439,18.99434 -0.09459,28.4915 -0.08212,19.51341 -0.136384,39.02681 -0.171948,58.54036 -3.137967,0.048 -6.275837,0.0763 -9.414062,0.10156 l -2.652588,0.0413 C 23.81406,222.81977 1.5565346,207.45478 -18.375829,188.75648 l -2.718735,-2.53775 c -8.235245,-7.71396 -16.371582,-15.53064 -24.472001,-23.38588 -5.420898,-5.25288 -10.871421,-10.46504 -16.411803,-15.59199 -41.322082,-38.29606 -41.811362,-38.47284 -39.697042,-61.82222 2.114319,-23.349383 3.306846,-25.445419 3.989278,-27.72417 6.748595,-20.960043 21.800751,-37.772405 41,-48.4375003 18.641419,-9.40752742 36.480818,-9.88154862 87.01386,-9.85750697 50.533043,0.0240417 73.811792,-1.09807633 92.064262,-0.98624303"
fill="#264863"
id="path4"
transform="translate(112.81113,8.4930303)"
sodipodi:nodetypes="cccccccccccccccccccc" />
<path
d="m 0,0 131.25,-0.25 v 63.875 l 0.11035,19.705566 c 0.0181,7.998291 0.0181,7.998291 0.0214,11.753754 0.006,2.598872 0.0186,5.19694 0.0405,7.79565 0.033,3.95755 0.0333,7.91478 0.0318,11.87247 l 0.0436,3.45275 c -0.0875,17.24733 -6.73078,32.78677 -18.61099,45.21278 C 106.22208,169.37917 98.630621,174.4762 90,177 l -11.397775,1.80377 -1.45256,0.16395 c -7.104685,-0.0441 -3.43355,-0.0556 -10.538337,-0.0776 -2.653987,-0.0101 -5.307964,-0.0237 -7.961914,-0.041 -3.807645,-0.0242 -7.615144,-0.0356 -11.422852,-0.0444 l -3.614349,-0.0313 -3.336334,-4.6e-4 -2.947357,-0.0135 c -2.521553,0.0131 41.626196,-2.01422 39.819227,0.21323 L 33,179 H 30.1875 24 c -1.532555,0.0113 -3.065107,0.0231 -4.597656,0.0352 -1.821614,0.009 -3.643229,0.0184 -5.464844,0.0273 l -2.798828,0.0254 -2.6738282,0.01 -2.4494629,0.0159 C 4,179 5.0139855,178.84803 4.0508683,177.7768 l 1.9580347,1.35049 -19.547353,-0.15833 -0.795115,0.0476 L -16,179 l -3.272949,0.0317 c -3.999316,0.0361 -7.998616,0.0591 -11.998047,0.0781 -1.732447,0.0101 -3.464876,0.0237 -5.197266,0.041 -2.487184,0.0242 -4.974145,0.0356 -7.461425,0.0444 l -2.35495,0.0313 c -1.906618,4e-4 -13.996389,0.0497 -15.899078,-0.0727 l 18.898307,-4.96534 C -32.858078,170.63158 -17.423649,159.0936 -8,141 c 5.0244847,-11.01947 7.29498468,-20.88967 7.31884766,-32.92383 l 0.0307674,-3.60735 c 0.0305076,-3.83335 0.0477557,-7.666634 0.0644474,-11.50007 0.0169446,-2.374681 0.0347624,-4.749356 0.0534668,-7.124023 C -0.4694063,77.063221 -0.4232179,68.2816 -0.375,59.5 Z"
fill="#773aaa"
id="path5"
transform="translate(177,231)"
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccc" />
<path
d="m 130.75,72.125 c 0,0 -130.9459176,38.46063 -130.5354588,-0.05093 L 130.75,72.125 l 0.5,86.75 L 0,159 0.2145412,72.074065"
fill="#ef7a34"
id="path6"
transform="translate(177,72)"
sodipodi:nodetypes="cccccc" />
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -31,6 +31,7 @@
<link rel="apple-touch-icon" sizes="144x144" href="icons/apple-icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="icons/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="icons/apple-icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="icons/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png"> <link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="icon" type="image/png" sizes="192x192" href="icons/android-icon-192x192.png"> <link rel="icon" type="image/png" sizes="192x192" href="icons/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png"> <link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">