714 lines
30 KiB
Dart
714 lines
30 KiB
Dart
import 'dart:html';
|
|
|
|
import 'package:auto_size_text/auto_size_text.dart';
|
|
import 'package:collection/collection.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_svg/svg.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:manager_app/Models/managerContext.dart';
|
|
import 'package:manager_app/Models/menu.dart';
|
|
import 'package:manager_app/Models/menuSection.dart';
|
|
import 'package:manager_app/Screens/ApiKeys/api_keys_screen.dart';
|
|
import 'package:manager_app/Screens/Configurations/configurations_screen.dart';
|
|
import 'package:manager_app/Screens/Kiosk_devices/kiosk_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/Applications/app_configuration_link_screen.dart';
|
|
import 'package:manager_app/Screens/Notifications/notifications_screen.dart';
|
|
import 'package:manager_app/Screens/Users/users_screen.dart';
|
|
import 'package:manager_app/l10n/app_localizations.dart';
|
|
import 'package:manager_app/app_context.dart';
|
|
import 'package:manager_app/Components/quota_bars_widget.dart';
|
|
import 'package:manager_app/constants.dart';
|
|
import 'package:manager_app/main.dart';
|
|
import 'package:manager_api_new/api.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
class MainScreen extends StatefulWidget {
|
|
final InstanceDTO instance;
|
|
final String? view;
|
|
MainScreen({Key? key, required this.instance, required this.view}) : super(key: key);
|
|
|
|
@override
|
|
_MainScreenState createState() => _MainScreenState();
|
|
}
|
|
|
|
class _MainScreenState extends State<MainScreen> {
|
|
late MenuSection devices;
|
|
late MenuSection configurations;
|
|
late MenuSection resources;
|
|
|
|
Menu menu = Menu(title: "MyInfoMate");
|
|
Widget? selectedElement = null;
|
|
|
|
final ValueNotifier<int?> currentPosition = ValueNotifier<int?>(null);
|
|
|
|
// Keys for nudge animation — one per section type
|
|
final _nudgeKeys = <String, GlobalKey<_NudgeIconState>>{};
|
|
GlobalKey<_NudgeIconState> _nudgeKey(String type) =>
|
|
_nudgeKeys.putIfAbsent(type, () => GlobalKey<_NudgeIconState>());
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
devices = MenuSection(name: "Applications", type: "devices", menuId: 0, subMenu: []);
|
|
if (widget.instance.isMobile!) devices.subMenu.add(MenuSection(name: "Mobile", type: "mobile", menuId: 1, subMenu: []));
|
|
if (widget.instance.isTablet!) devices.subMenu.add(MenuSection(name: "Kiosk", type: "kiosk", menuId: 2, subMenu: []));
|
|
if (widget.instance.isWeb!) devices.subMenu.add(MenuSection(name: "Web", type: "web", menuId: 3, subMenu: []));
|
|
if (widget.instance.isVR!) devices.subMenu.add(MenuSection(name: "VR", type: "vr", menuId: 4, subMenu: []));
|
|
|
|
configurations = MenuSection(name: "Configurations", type: "configurations", menuId: 5, subMenu: []);
|
|
resources = MenuSection(name: "Ressources", type: "resources", menuId: 6, subMenu: []);
|
|
|
|
menu.sections = [devices, configurations, resources];
|
|
if (widget.instance.isStatistic == true) {
|
|
menu.sections!.add(MenuSection(name: "Statistiques", type: "statistics", menuId: 7, subMenu: []));
|
|
}
|
|
|
|
|
|
if(currentPosition.value == null) {
|
|
if (widget.instance.isMobile!) {
|
|
currentPosition.value = 1;
|
|
} else if (widget.instance.isTablet!) {
|
|
currentPosition.value = 2;
|
|
} else if (widget.instance.isWeb!) {
|
|
currentPosition.value = 3;
|
|
} else {
|
|
currentPosition.value = 4;
|
|
}
|
|
} else {
|
|
//currentPosition = managerAppContext.currentPositionMenu!;
|
|
}
|
|
|
|
selectedElement = initElementToShow(context, widget.view, currentPosition.value!, menu, widget.instance);
|
|
}
|
|
|
|
Future<void> _showAssignPlanDialog(BuildContext context, ManagerAppContext managerCtx, Instance inst, {void Function()? onSaved}) async {
|
|
List<SubscriptionPlanDTO>? plans;
|
|
InstanceDTO? instanceDetail;
|
|
try {
|
|
plans = await managerCtx.clientAPI!.subscriptionPlanApi!.subscriptionPlanGet();
|
|
instanceDetail = await managerCtx.clientAPI!.instanceApi!.instanceGetDetail(inst.id);
|
|
} catch (_) {}
|
|
|
|
if (!context.mounted) return;
|
|
|
|
final planList = plans ?? [];
|
|
String? selectedPlanId = instanceDetail?.subscriptionPlanId;
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (dialogContext) => StatefulBuilder(
|
|
builder: (dialogContext, setDialogState) => AlertDialog(
|
|
title: Text('Plan — ${inst.name}'),
|
|
content: SizedBox(
|
|
width: 360,
|
|
child: planList.isEmpty
|
|
? Text(AppLocalizations.of(context)!.noPlansAvailable)
|
|
: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
RadioListTile<String?>(
|
|
title: Text(AppLocalizations.of(context)!.noPlan),
|
|
value: null,
|
|
groupValue: selectedPlanId,
|
|
onChanged: (v) => setDialogState(() => selectedPlanId = v),
|
|
),
|
|
...planList.map((plan) => RadioListTile<String?>(
|
|
title: Text(plan.name),
|
|
subtitle: Text(
|
|
'${_formatQuotaBytes(plan.storageQuotaBytes, unlimitedLabel: AppLocalizations.of(dialogContext)!.unlimitedStorage)} · ${plan.aiRequestsPerMonth == 0 ? AppLocalizations.of(dialogContext)!.unlimitedAI : AppLocalizations.of(dialogContext)!.aiRequestsPerMonth(plan.aiRequestsPerMonth ?? 0)}',
|
|
style: const TextStyle(fontSize: 11),
|
|
),
|
|
value: plan.id,
|
|
groupValue: selectedPlanId,
|
|
onChanged: (v) => setDialogState(() => selectedPlanId = v),
|
|
)),
|
|
],
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(dialogContext, rootNavigator: true).pop(),
|
|
child: Text(AppLocalizations.of(dialogContext)!.cancel),
|
|
),
|
|
TextButton(
|
|
onPressed: () async {
|
|
Navigator.of(dialogContext, rootNavigator: true).pop();
|
|
try {
|
|
await managerCtx.clientAPI!.instanceApi!.instanceUpdateinstance(
|
|
InstanceDTO(
|
|
id: instanceDetail?.id,
|
|
name: instanceDetail?.name,
|
|
pinCode: instanceDetail?.pinCode,
|
|
isPushNotification: instanceDetail?.isPushNotification,
|
|
isStatistic: instanceDetail?.isStatistic,
|
|
isMobile: instanceDetail?.isMobile,
|
|
isTablet: instanceDetail?.isTablet,
|
|
isWeb: instanceDetail?.isWeb,
|
|
isVR: instanceDetail?.isVR,
|
|
isAssistant: instanceDetail?.isAssistant,
|
|
aiRequestsThisMonth: instanceDetail?.aiRequestsThisMonth,
|
|
aiUsageMonthKey: instanceDetail?.aiUsageMonthKey,
|
|
subscriptionPlanId: selectedPlanId ?? '',
|
|
),
|
|
);
|
|
onSaved?.call();
|
|
if (context.mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.planUpdated)));
|
|
}
|
|
} catch (e) {
|
|
if (context.mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.errorMessage(e.toString()))));
|
|
}
|
|
}
|
|
},
|
|
child: Text(AppLocalizations.of(dialogContext)!.save),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
static String _formatQuotaBytes(int? bytes, {String? unlimitedLabel}) {
|
|
if (bytes == null || bytes == 0) return unlimitedLabel ?? 'Stockage illimité';
|
|
if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(0)} MB';
|
|
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
|
|
}
|
|
|
|
Future<void> _showSwitchInstanceDialog(BuildContext context, ManagerAppContext managerCtx) async {
|
|
List<Instance>? instances;
|
|
try {
|
|
instances = await managerCtx.clientAPI!.instanceApi!.instanceGet();
|
|
} catch (_) {}
|
|
|
|
if (!context.mounted) return;
|
|
|
|
final instanceList = instances ?? [];
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (_) => AlertDialog(
|
|
title: Text(AppLocalizations.of(context)!.switchInstance),
|
|
content: SizedBox(
|
|
width: 400,
|
|
child: instanceList.isEmpty
|
|
? Text(AppLocalizations.of(context)!.noInstanceFound)
|
|
: ListView.builder(
|
|
shrinkWrap: true,
|
|
itemCount: instanceList.length,
|
|
itemBuilder: (_, i) {
|
|
final inst = instanceList[i];
|
|
final isCurrent = inst.id == managerCtx.instanceId;
|
|
return ListTile(
|
|
leading: Icon(
|
|
Icons.business,
|
|
color: isCurrent ? kPrimaryColor : kBodyTextColor,
|
|
),
|
|
title: Text(
|
|
inst.name,
|
|
style: TextStyle(
|
|
color: isCurrent ? kPrimaryColor : kBodyTextColor,
|
|
fontWeight: isCurrent ? FontWeight.bold : FontWeight.normal,
|
|
),
|
|
),
|
|
trailing: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (isCurrent) const Icon(Icons.check, color: Colors.green),
|
|
IconButton(
|
|
icon: const Icon(Icons.edit_outlined, size: 18),
|
|
tooltip: AppLocalizations.of(context)!.configurePlan,
|
|
onPressed: () => _showAssignPlanDialog(context, managerCtx, inst, onSaved: () {
|
|
Navigator.of(context, rootNavigator: true).pop();
|
|
}),
|
|
),
|
|
],
|
|
),
|
|
onTap: isCurrent
|
|
? null
|
|
: () async {
|
|
Navigator.of(context, rootNavigator: true).pop();
|
|
final newInstance = await managerCtx.clientAPI!.instanceApi!.instanceGetDetail(inst.id);
|
|
if (newInstance != null && context.mounted) {
|
|
setState(() {
|
|
managerCtx.instanceId = newInstance.id;
|
|
managerCtx.instanceDTO = newInstance;
|
|
menu.sections = [];
|
|
});
|
|
final view = newInstance.isMobile! ? 'mobile'
|
|
: newInstance.isTablet! ? 'kiosk'
|
|
: newInstance.isWeb! ? 'web'
|
|
: 'vr';
|
|
context.go('/main/$view');
|
|
}
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(onPressed: () => Navigator.of(context, rootNavigator: true).pop(), child: Text(AppLocalizations.of(context)!.close)),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
static String _localizedSectionName(BuildContext context, String type) {
|
|
final l = AppLocalizations.of(context)!;
|
|
switch (type) {
|
|
case 'devices': return l.menuApplications;
|
|
case 'configurations': return l.menuConfigurations;
|
|
case 'resources': return l.menuResources;
|
|
case 'statistics': return l.menuStatistics;
|
|
case 'notifications': return l.menuNotifications;
|
|
case 'users': return l.menuUsers;
|
|
case 'apikeys': return l.menuApiKeys;
|
|
default: return type;
|
|
}
|
|
}
|
|
|
|
static IconData _sectionIcon(String type) {
|
|
switch (type) {
|
|
case 'devices': return Icons.apps;
|
|
case 'configurations': return Icons.settings_outlined;
|
|
case 'resources': return Icons.folder_open_outlined;
|
|
case 'statistics': return Icons.bar_chart;
|
|
case 'notifications': return Icons.notifications_none;
|
|
case 'users': return Icons.people_outline;
|
|
case 'apikeys': return Icons.vpn_key_outlined;
|
|
default: return Icons.circle_outlined;
|
|
}
|
|
}
|
|
|
|
Widget buildMenu(BuildContext context, AppContext appContext, ManagerAppContext managerAppContext, bool isDrawer) {
|
|
return Container(
|
|
width: isDrawer ? null : 250, // fixed width on sidebar, null on drawer for full width
|
|
child: Card(
|
|
color: kWhite,
|
|
margin: const EdgeInsets.symmetric(vertical: 8),
|
|
elevation: 0,
|
|
child: Column(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Container(
|
|
height: 150,
|
|
width: 250,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
constraints: BoxConstraints(maxHeight: 60, minHeight: 40),
|
|
child: SvgPicture.asset('assets/images/MyInfoMate_logo_only.svg')
|
|
),
|
|
SizedBox(height: 16),
|
|
Text(
|
|
menu.title,
|
|
style: TextStyle(
|
|
color: kPrimaryColor,
|
|
fontSize: 25,
|
|
fontWeight: FontWeight.w400,
|
|
fontFamily: "Helvetica",
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
SizedBox(height: 25),
|
|
Expanded(
|
|
child: Theme(
|
|
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
|
|
child: ListView(
|
|
padding: EdgeInsets.zero,
|
|
children: menu.sections!.map((section) {
|
|
final router = GoRouter.of(context);
|
|
final routeMatchList = router.routerDelegate.currentConfiguration;
|
|
var currentPath = routeMatchList.isNotEmpty ? routeMatchList.matches.first.matchedLocation : null;
|
|
|
|
if (section.subMenu.isEmpty) {
|
|
return Container(
|
|
key: ValueKey(section.type),
|
|
decoration: currentPath!.contains(section.type)
|
|
? BoxDecoration(
|
|
border: Border(
|
|
right: BorderSide(
|
|
color: kPrimaryColor,
|
|
width: 2,
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
child: ListTile(
|
|
leading: _NudgeIcon(
|
|
key: _nudgeKey(section.type),
|
|
icon: _sectionIcon(section.type),
|
|
isActive: currentPath.contains(section.type),
|
|
color: currentPath.contains(section.type) ? kPrimaryColor : kBodyTextColor,
|
|
size: 22,
|
|
),
|
|
title: Text(_localizedSectionName(context, section.type), style: TextStyle(color: currentPath.contains(section.type) ? kPrimaryColor : kBodyTextColor, fontSize: 22, fontWeight: currentPath.contains(section.type) ? FontWeight.w500 : FontWeight.w100)),
|
|
selected: currentPosition == section.menuId,
|
|
onTap: () {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) =>
|
|
_nudgeKey(section.type).currentState?.nudge());
|
|
context.go('/main/${section.type}');
|
|
if (isDrawer) Navigator.of(context).pop();
|
|
},
|
|
),
|
|
);
|
|
} else {
|
|
final isAppActive = currentPath!.contains("mobile") || currentPath.contains("kiosk") || currentPath.contains("web") || currentPath.contains("vr");
|
|
return Container(
|
|
key: ValueKey(section.type),
|
|
child: ExpansionTile(
|
|
leading: _NudgeIcon(
|
|
key: _nudgeKey(section.type),
|
|
icon: _sectionIcon(section.type),
|
|
isActive: isAppActive,
|
|
color: isAppActive ? kPrimaryColor : kBodyTextColor,
|
|
size: 22,
|
|
),
|
|
iconColor: isAppActive ? kPrimaryColor : kBodyTextColor,
|
|
collapsedIconColor: isAppActive ? kPrimaryColor : kBodyTextColor,
|
|
title: Text(_localizedSectionName(context, section.type), style: TextStyle(color: isAppActive ? kPrimaryColor : kBodyTextColor, fontSize: 22, fontWeight: isAppActive ? FontWeight.w500 : FontWeight.w100)),
|
|
children: section.subMenu.map((subSection) {
|
|
return Container(
|
|
decoration: currentPath.contains(subSection.type)
|
|
? BoxDecoration(
|
|
border: Border(
|
|
right: BorderSide(
|
|
color: kPrimaryColor,
|
|
width: 2,
|
|
),
|
|
),
|
|
) : null,
|
|
child: ListTile(
|
|
title: Padding(
|
|
padding: const EdgeInsets.only(left: 16.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
subSection.type == "mobile" ? Icon(Icons.phone_iphone, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) :
|
|
subSection.type == "kiosk" ? Icon(Icons.tablet_rounded, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) :
|
|
subSection.type == "web" ? Icon(Icons.public_outlined, color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, size: 20) :
|
|
subSection.type == "vr" ? Icon(Icons.panorama_photosphere, color: currentPath.contains(subSection.type)? kPrimaryColor : kBodyTextColor, size: 20) : SizedBox(),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Text(_localizedSectionName(context, subSection.type), style: TextStyle(color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, fontSize: 18)),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
selected: currentPosition.value == subSection.menuId,
|
|
onTap: () {
|
|
if (currentPath != null && currentPath.contains(subSection.type)) {
|
|
// already on this page
|
|
} else {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) =>
|
|
_nudgeKey(section.type).currentState?.nudge());
|
|
context.go('/main/${subSection.type}');
|
|
}
|
|
if (isDrawer) Navigator.of(context).pop();
|
|
},
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
);
|
|
}
|
|
}).toList(),
|
|
),
|
|
),
|
|
),
|
|
// Footer: Quota + Language + Email + Switch instance (SuperAdmin) + Logout
|
|
Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Column(
|
|
children: [
|
|
const QuotaBarsWidget(),
|
|
DropdownButton<Locale>(
|
|
value: managerAppContext.locale,
|
|
underline: const SizedBox(),
|
|
isDense: true,
|
|
items: const [
|
|
DropdownMenuItem(value: Locale('fr'), child: Text('🇫🇷 FR')),
|
|
DropdownMenuItem(value: Locale('en'), child: Text('🇬🇧 EN')),
|
|
DropdownMenuItem(value: Locale('nl'), child: Text('🇧🇪 NL')),
|
|
],
|
|
onChanged: (locale) {
|
|
if (locale != null) appContext.setLocale(locale);
|
|
},
|
|
),
|
|
const SizedBox(height: 4),
|
|
AutoSizeText(
|
|
(appContext.getContext() as ManagerAppContext).email ?? "",
|
|
style: TextStyle(color: kBodyTextColor, fontSize: 16, fontWeight: FontWeight.w300, fontFamily: "Helvetica"),
|
|
maxLines: 1,
|
|
),
|
|
if ((appContext.getContext() as ManagerAppContext).role == UserRole.SuperAdmin)
|
|
IconButton(
|
|
icon: Icon(Icons.swap_horiz, color: kPrimaryColor),
|
|
tooltip: AppLocalizations.of(context)!.tooltipSwitchInstance,
|
|
onPressed: () => _showSwitchInstanceDialog(context, appContext.getContext() as ManagerAppContext),
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.logout, color: kPrimaryColor),
|
|
onPressed: () async {
|
|
var session = await loadJsonSessionFile();
|
|
setState(() {
|
|
Storage localStorage = window.localStorage;
|
|
localStorage.clear();
|
|
ManagerAppContext managerAppContext = appContext.getContext();
|
|
managerAppContext.accessToken = null;
|
|
managerAppContext.instanceId = null;
|
|
managerAppContext.instanceDTO = null;
|
|
appContext.setContext(managerAppContext);
|
|
|
|
context.go('/login');
|
|
});
|
|
},
|
|
)
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final appContext = Provider.of<AppContext>(context);
|
|
ManagerAppContext managerAppContext = appContext.getContext();
|
|
|
|
// Synchronise les items de menu sensibles au rôle à chaque rebuild
|
|
final role = managerAppContext.role;
|
|
final hasAdminItems = menu.sections!.any((s) => s.menuId == 8);
|
|
final hasNotifItem = menu.sections!.any((s) => s.menuId == 10);
|
|
if (role != null && role.value <= 1 && managerAppContext.instanceDTO?.isPushNotification == true && !hasNotifItem) {
|
|
menu.sections!.add(MenuSection(name: "Notifications", type: "notifications", menuId: 10, subMenu: []));
|
|
} else if ((role == null || role.value > 1 || managerAppContext.instanceDTO?.isPushNotification != true) && hasNotifItem) {
|
|
menu.sections!.removeWhere((s) => s.menuId == 10);
|
|
}
|
|
if (role != null && role.value <= 1 && !hasAdminItems) {
|
|
menu.sections!.add(MenuSection(name: "Utilisateurs", type: "users", menuId: 8, subMenu: []));
|
|
menu.sections!.add(MenuSection(name: "Clés API", type: "apikeys", menuId: 9, subMenu: []));
|
|
} else if ((role == null || role.value > 1) && hasAdminItems) {
|
|
menu.sections!.removeWhere((s) => s.menuId == 8 || s.menuId == 9);
|
|
}
|
|
|
|
Size size = MediaQuery.of(context).size;
|
|
bool isMobile = size.width < 850;
|
|
|
|
return Scaffold(
|
|
appBar: isMobile
|
|
? AppBar(
|
|
title: Text(menu.title, style: TextStyle(color: kWhite)),
|
|
backgroundColor: kPrimaryColor,
|
|
leading: Builder(
|
|
builder: (context) => IconButton(
|
|
icon: Icon(Icons.menu, color: kWhite),
|
|
onPressed: () => Scaffold.of(context).openDrawer(),
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
drawer: isMobile ? ValueListenableBuilder<int?>(
|
|
valueListenable: currentPosition,
|
|
builder: (context, value, _) {
|
|
return Drawer(child: buildMenu(context, appContext, managerAppContext, true));
|
|
}
|
|
) : null,
|
|
body: Row(
|
|
children: [
|
|
if (!isMobile)
|
|
ValueListenableBuilder<int?>(
|
|
valueListenable: currentPosition,
|
|
builder: (context, value, _) {
|
|
return buildMenu(context, appContext, managerAppContext, false);
|
|
}
|
|
),
|
|
Expanded(
|
|
child: ValueListenableBuilder<int?>(
|
|
valueListenable: currentPosition,
|
|
builder: (context, value, _) {
|
|
selectedElement = initElementToShow(context, widget.view, currentPosition.value!, menu, widget.instance);
|
|
return Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: selectedElement,
|
|
);
|
|
}
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
initElementToShow(BuildContext context, String? view, int currentPosition, Menu menu, InstanceDTO instanceDTO) {
|
|
|
|
if(view != null) {
|
|
switch (view) {
|
|
case "mobile":
|
|
currentPosition = 1;
|
|
break;
|
|
case "kiosk":
|
|
currentPosition = 2;
|
|
break;
|
|
case "web":
|
|
currentPosition = 3;
|
|
break;
|
|
case "vr":
|
|
currentPosition = 4;
|
|
break;
|
|
case "configurations":
|
|
currentPosition = 5;
|
|
break;
|
|
case "resources":
|
|
currentPosition = 6;
|
|
break;
|
|
case "statistics":
|
|
currentPosition = 7;
|
|
break;
|
|
case "users":
|
|
currentPosition = 8;
|
|
break;
|
|
case "apikeys":
|
|
currentPosition = 9;
|
|
break;
|
|
case "notifications":
|
|
currentPosition = 10;
|
|
break;
|
|
}
|
|
}
|
|
|
|
MenuSection? elementToShow = menu.sections!.firstWhereOrNull((s) => s.menuId == currentPosition);
|
|
|
|
if(elementToShow == null) {
|
|
elementToShow = menu.sections![0].subMenu.where((s) => s.menuId == currentPosition).first;
|
|
}
|
|
|
|
switch (elementToShow.type) {
|
|
case 'mobile' :
|
|
var applicationInstanceMobile = instanceDTO.applicationInstanceDTOs!.firstWhere((ai) => ai.appType == AppType.Mobile);
|
|
return Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: AppConfigurationLinkScreen(applicationInstanceDTO: applicationInstanceMobile)
|
|
);
|
|
case 'kiosk' :
|
|
var applicationInstanceTablet = instanceDTO.applicationInstanceDTOs!.firstWhere((ai) => ai.appType == AppType.Tablet);
|
|
return Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: KioskScreen(applicationInstanceDTO: applicationInstanceTablet)
|
|
);
|
|
case 'web' :
|
|
var applicationInstanceWeb = instanceDTO.applicationInstanceDTOs!.firstWhere((ai) => ai.appType == AppType.Web);
|
|
return Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Text("TODO web")
|
|
);
|
|
case 'vr' :
|
|
var applicationInstanceVR = instanceDTO.applicationInstanceDTOs!.firstWhere((ai) => ai.appType == AppType.VR);
|
|
return Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Text("TODO vr")
|
|
);
|
|
case 'configurations' :
|
|
return Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: ConfigurationsScreen()
|
|
);
|
|
case 'resources' :
|
|
return Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: ResourcesScreen(
|
|
resourceTypes: [
|
|
ResourceType.Audio,
|
|
ResourceType.Image,
|
|
ResourceType.ImageUrl,
|
|
ResourceType.Video,
|
|
ResourceType.VideoUrl,
|
|
ResourceType.Pdf,
|
|
ResourceType.Json,
|
|
ResourceType.JsonUrl
|
|
]
|
|
)
|
|
);
|
|
case 'statistics' :
|
|
return const Padding(
|
|
padding: EdgeInsets.all(8.0),
|
|
child: StatisticsScreen()
|
|
);
|
|
case 'users':
|
|
return const Padding(
|
|
padding: EdgeInsets.all(8.0),
|
|
child: UsersScreen()
|
|
);
|
|
case 'apikeys':
|
|
return const Padding(
|
|
padding: EdgeInsets.all(8.0),
|
|
child: ApiKeysScreen()
|
|
);
|
|
case 'notifications':
|
|
return const Padding(
|
|
padding: EdgeInsets.all(8.0),
|
|
child: NotificationsScreen()
|
|
);
|
|
default:
|
|
return Text('Hellow default');
|
|
}
|
|
}
|
|
|
|
class _NudgeIcon extends StatefulWidget {
|
|
final IconData icon;
|
|
final bool isActive;
|
|
final Color color;
|
|
final double size;
|
|
|
|
const _NudgeIcon({Key? key, required this.icon, required this.isActive, required this.color, required this.size}) : super(key: key);
|
|
|
|
@override
|
|
State<_NudgeIcon> createState() => _NudgeIconState();
|
|
}
|
|
|
|
class _NudgeIconState extends State<_NudgeIcon> with SingleTickerProviderStateMixin {
|
|
late final AnimationController _ctrl;
|
|
late final Animation<double> _dx;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 400));
|
|
_dx = TweenSequence<double>([
|
|
TweenSequenceItem(tween: Tween(begin: 0.0, end: 5.0), weight: 35),
|
|
TweenSequenceItem(tween: Tween(begin: 5.0, end: 0.0), weight: 65),
|
|
]).animate(CurvedAnimation(parent: _ctrl, curve: Curves.easeOut));
|
|
}
|
|
|
|
void nudge() => _ctrl.forward(from: 0);
|
|
|
|
@override
|
|
void dispose() {
|
|
_ctrl.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: _dx,
|
|
builder: (_, child) => Transform.translate(
|
|
offset: Offset(_dx.value, 0),
|
|
child: child,
|
|
),
|
|
child: Icon(widget.icon, color: widget.color, size: widget.size),
|
|
);
|
|
}
|
|
} |