Multi langues + Assign plan dialog + display quotas + translator IA
This commit is contained in:
parent
634fd8d4d7
commit
0059582e9f
4
l10n.yaml
Normal file
4
l10n.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
arb-dir: lib/l10n
|
||||
template-arb-file: app_fr.arb
|
||||
output-localization-file: app_localizations.dart
|
||||
output-class: AppLocalizations
|
||||
@ -13,4 +13,4 @@ class CommonLoader extends StatelessWidget {
|
||||
child: LoaderWaveFloat(size: size),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ class MultiSelectContainer extends StatelessWidget {
|
||||
if(label != null)
|
||||
Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: Text(label!, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
|
||||
child: Text(label!, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: kPrimaryColor))
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
@ -95,7 +95,10 @@ class _MultiSelectChipState extends State<MultiSelectChip> {
|
||||
choices.add(Container(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: ChoiceChip(
|
||||
label: Text(widget.isHTMLLabel ? parse(item).documentElement!.text : item, style: TextStyle(color: kBlack)),
|
||||
label: Text(
|
||||
widget.isHTMLLabel ? parse(item).documentElement!.text : item,
|
||||
style: TextStyle(color: widget.selectedValues.contains(item) ? kWhite : kPrimaryColor),
|
||||
),
|
||||
selected: widget.selectedValues.contains(item),
|
||||
selectedColor: kPrimaryColor,
|
||||
onSelected: (selected) {
|
||||
|
||||
@ -25,8 +25,10 @@ showMultiStringInputHTML (String label, String modalLabel, bool isTitle, List<Tr
|
||||
children: [
|
||||
Center(child: Text(modalLabel, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500))),
|
||||
const SizedBox(height: 16),
|
||||
SingleChildScrollView(
|
||||
child: TranslationInputContainer(isTitle: isTitle, values: values, newValues: newValues, onGetResult: onGetResult, maxLines: maxLines, resourceTypes: resourceTypes),
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
child: TranslationInputContainer(isTitle: isTitle, values: values, newValues: newValues, onGetResult: onGetResult, maxLines: maxLines, resourceTypes: resourceTypes),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
@ -99,8 +101,10 @@ showMultiStringInputAndResourceHTML (String label, String modalLabel, bool isTit
|
||||
children: [
|
||||
Center(child: Text(modalLabel, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500))),
|
||||
const SizedBox(height: 16),
|
||||
SingleChildScrollView(
|
||||
child: TranslationInputAndResourceContainer(isTitle: isTitle, values: values, newValues: newValues, onGetResult: onGetResult, maxLines: maxLines, resourceTypes: resourceTypes),
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
child: TranslationInputAndResourceContainer(isTitle: isTitle, values: values, newValues: newValues, onGetResult: onGetResult, maxLines: maxLines, resourceTypes: resourceTypes),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
|
||||
172
lib/Components/quota_bars_widget.dart
Normal file
172
lib/Components/quota_bars_widget.dart
Normal file
@ -0,0 +1,172 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../app_context.dart';
|
||||
import '../Models/managerContext.dart';
|
||||
import '../constants.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class QuotaBarsWidget extends StatefulWidget {
|
||||
const QuotaBarsWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<QuotaBarsWidget> createState() => _QuotaBarsWidgetState();
|
||||
}
|
||||
|
||||
class _QuotaBarsWidgetState extends State<QuotaBarsWidget> {
|
||||
InstanceQuotaDTO? _quota;
|
||||
bool _loading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _fetchQuota());
|
||||
}
|
||||
|
||||
Future<void> _fetchQuota() async {
|
||||
final managerContext = Provider.of<AppContext>(context, listen: false).getContext() as ManagerAppContext;
|
||||
final instanceId = managerContext.instanceId;
|
||||
final client = managerContext.clientAPI;
|
||||
|
||||
if (instanceId == null || client == null) {
|
||||
setState(() => _loading = false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final quota = await client.instanceApi!.instanceGetQuota(instanceId);
|
||||
if (mounted) setState(() { _quota = quota; _loading = false; });
|
||||
} catch (_) {
|
||||
if (mounted) setState(() => _loading = false);
|
||||
}
|
||||
}
|
||||
|
||||
Color _barColor(int used, int quota) {
|
||||
if (quota == 0) return kPrimaryColor;
|
||||
final ratio = used / quota;
|
||||
if (ratio >= 0.95) return Colors.red;
|
||||
if (ratio >= 0.80) return Colors.orange;
|
||||
return kPrimaryColor;
|
||||
}
|
||||
|
||||
double? _barValue(int used, int quota) {
|
||||
if (quota == 0) return null;
|
||||
return (used / quota).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
String _formatBytes(int bytes) {
|
||||
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(0)} KB';
|
||||
if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final managerContext = Provider.of<AppContext>(context).getContext() as ManagerAppContext;
|
||||
final isAssistant = managerContext.instanceDTO?.isAssistant ?? false;
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
if (_loading) {
|
||||
return const SizedBox(
|
||||
height: 8,
|
||||
child: LinearProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (_quota == null) return const SizedBox.shrink();
|
||||
|
||||
final storageUsed = _quota!.storageUsedBytes ?? 0;
|
||||
final storageQuota = _quota!.storageQuotaBytes ?? 0;
|
||||
final aiUsed = _quota!.aiRequestsUsed ?? 0;
|
||||
final aiQuota = _quota!.aiRequestsPerMonth ?? 0;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_QuotaBar(
|
||||
icon: Icons.storage,
|
||||
label: l10n.storageLabel,
|
||||
value: _barValue(storageUsed, storageQuota),
|
||||
color: _barColor(storageUsed, storageQuota),
|
||||
subtitle: storageQuota == 0
|
||||
? '${_formatBytes(storageUsed)} · ${l10n.unlimited}'
|
||||
: '${_formatBytes(storageUsed)} / ${_formatBytes(storageQuota)}',
|
||||
),
|
||||
if (isAssistant) ...[
|
||||
const SizedBox(height: 8),
|
||||
_QuotaBar(
|
||||
icon: Icons.chat_bubble_outline,
|
||||
label: l10n.aiThisMonthLabel,
|
||||
value: _barValue(aiUsed, aiQuota),
|
||||
color: _barColor(aiUsed, aiQuota),
|
||||
subtitle: aiQuota == 0
|
||||
? '$aiUsed ${l10n.requestsAbbr} · ${l10n.unlimited}'
|
||||
: '$aiUsed / $aiQuota ${l10n.requestsAbbr}.',
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _QuotaBar extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final double? value;
|
||||
final Color color;
|
||||
final String subtitle;
|
||||
|
||||
const _QuotaBar({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.color,
|
||||
required this.subtitle,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(icon, size: 14, color: kBodyTextColor),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: kBodyTextColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: 'Helvetica',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: LinearProgressIndicator(
|
||||
value: value,
|
||||
minHeight: 5,
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(color),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: kBodyTextColor.withValues(alpha: 0.6),
|
||||
fontFamily: 'Helvetica',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,15 @@ import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart
|
||||
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/Components/resource_input_container.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart' show ManagerAppContext;
|
||||
import 'package:manager_app/Services/ai_translate_service.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'flag_decoration.dart';
|
||||
import 'message_notification.dart';
|
||||
|
||||
class TranslationInputAndResourceContainer extends StatefulWidget {
|
||||
TranslationInputAndResourceContainer({
|
||||
@ -35,6 +41,7 @@ class _TranslationInputAndResourceContainerState
|
||||
extends State<TranslationInputAndResourceContainer> {
|
||||
late Map<String, QuillController> _controllers;
|
||||
bool _isEnforcingLimit = false;
|
||||
bool _isTranslating = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -92,6 +99,65 @@ class _TranslationInputAndResourceContainerState
|
||||
).convert();
|
||||
}
|
||||
|
||||
Future<void> _translateWithAI() async {
|
||||
final appContext = context.read<AppContext>().getContext() as ManagerAppContext;
|
||||
if (appContext.instanceDTO?.isAssistant != true) return;
|
||||
|
||||
final source = widget.newValues.firstWhere(
|
||||
(t) => t.value != null && t.value!.trim().isNotEmpty && t.value!.trim() != '<p><br></p>',
|
||||
orElse: () => widget.newValues.first,
|
||||
);
|
||||
|
||||
if (source.value == null || source.value!.trim().isEmpty) {
|
||||
showNotification(kError, kWhite, 'Aucun texte source à traduire', context, null);
|
||||
return;
|
||||
}
|
||||
|
||||
final targetLangs = widget.newValues
|
||||
.where((t) => t.language != source.language)
|
||||
.map((t) => t.language!)
|
||||
.toList();
|
||||
|
||||
if (targetLangs.isEmpty) return;
|
||||
|
||||
setState(() => _isTranslating = true);
|
||||
|
||||
try {
|
||||
final translations = await AiTranslateService.translate(
|
||||
host: appContext.host!,
|
||||
accessToken: appContext.accessToken!,
|
||||
instanceId: appContext.instanceId!,
|
||||
text: source.value!,
|
||||
sourceLang: source.language!,
|
||||
targetLangs: targetLangs,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
for (final translation in widget.newValues) {
|
||||
final lang = translation.language!;
|
||||
if (lang == source.language) continue;
|
||||
final translated = translations[lang];
|
||||
if (translated == null) continue;
|
||||
// On ne touche qu'au texte, pas à la ressource liée
|
||||
translation.value = translated;
|
||||
_controllers[lang]?.dispose();
|
||||
final controller = QuillController(
|
||||
document: Document.fromJson(_htmlToDeltaJson(translated)),
|
||||
selection: const TextSelection.collapsed(offset: 0),
|
||||
);
|
||||
_setupListener(lang, controller);
|
||||
_controllers[lang] = controller;
|
||||
}
|
||||
});
|
||||
|
||||
showNotification(kSuccess, kWhite, 'Traduction appliquée', context, null);
|
||||
} catch (e) {
|
||||
showNotification(kError, kWhite, 'Erreur lors de la traduction IA', context, null);
|
||||
} finally {
|
||||
setState(() => _isTranslating = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final c in _controllers.values) {
|
||||
@ -104,7 +170,29 @@ class _TranslationInputAndResourceContainerState
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: widget.newValues.map((t) => _buildLanguageSection(t)).toList(),
|
||||
children: [
|
||||
...widget.newValues.map((t) => _buildLanguageSection(t)),
|
||||
const SizedBox(height: 8),
|
||||
Builder(builder: (ctx) {
|
||||
final instance = ctx.watch<AppContext>().getContext()?.instanceDTO;
|
||||
if (instance?.isAssistant != true) return const SizedBox.shrink();
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 370,
|
||||
height: 70,
|
||||
child: _isTranslating
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: RoundedButton(
|
||||
text: "Traduire via IA",
|
||||
icon: Icons.auto_awesome,
|
||||
color: kPrimaryColor,
|
||||
press: _translateWithAI,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -5,10 +5,14 @@ import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/Components/resource_input_container.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart' show ManagerAppContext;
|
||||
import 'package:manager_app/Services/ai_translate_service.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'flag_decoration.dart';
|
||||
import 'message_notification.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
|
||||
class TranslationInputContainer extends StatefulWidget {
|
||||
TranslationInputContainer({
|
||||
@ -35,6 +39,7 @@ class TranslationInputContainer extends StatefulWidget {
|
||||
class _TranslationInputContainerState extends State<TranslationInputContainer> {
|
||||
late Map<String, QuillController> _controllers;
|
||||
bool _isEnforcingLimit = false;
|
||||
bool _isTranslating = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -92,6 +97,64 @@ class _TranslationInputContainerState extends State<TranslationInputContainer> {
|
||||
).convert();
|
||||
}
|
||||
|
||||
Future<void> _translateWithAI() async {
|
||||
final appContext = context.read<AppContext>().getContext() as ManagerAppContext;
|
||||
if (appContext.instanceDTO?.isAssistant != true) return;
|
||||
|
||||
final source = widget.newValues.firstWhere(
|
||||
(t) => t.value != null && t.value!.trim().isNotEmpty && t.value!.trim() != '<p><br></p>',
|
||||
orElse: () => widget.newValues.first,
|
||||
);
|
||||
|
||||
if (source.value == null || source.value!.trim().isEmpty) {
|
||||
showNotification(kError, kWhite, 'Aucun texte source à traduire', context, null);
|
||||
return;
|
||||
}
|
||||
|
||||
final targetLangs = widget.newValues
|
||||
.where((t) => t.language != source.language)
|
||||
.map((t) => t.language!)
|
||||
.toList();
|
||||
|
||||
if (targetLangs.isEmpty) return;
|
||||
|
||||
setState(() => _isTranslating = true);
|
||||
|
||||
try {
|
||||
final translations = await AiTranslateService.translate(
|
||||
host: appContext.host!,
|
||||
accessToken: appContext.accessToken!,
|
||||
instanceId: appContext.instanceId!,
|
||||
text: source.value!,
|
||||
sourceLang: source.language!,
|
||||
targetLangs: targetLangs,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
for (final translation in widget.newValues) {
|
||||
final lang = translation.language!;
|
||||
if (lang == source.language) continue;
|
||||
final translated = translations[lang];
|
||||
if (translated == null) continue;
|
||||
translation.value = translated;
|
||||
_controllers[lang]?.dispose();
|
||||
final controller = QuillController(
|
||||
document: Document.fromJson(_htmlToDeltaJson(translated)),
|
||||
selection: const TextSelection.collapsed(offset: 0),
|
||||
);
|
||||
_setupListener(lang, controller);
|
||||
_controllers[lang] = controller;
|
||||
}
|
||||
});
|
||||
|
||||
showNotification(kSuccess, kWhite, 'Traduction appliquée', context, null);
|
||||
} catch (e) {
|
||||
showNotification(kError, kWhite, 'Erreur lors de la traduction IA', context, null);
|
||||
} finally {
|
||||
setState(() => _isTranslating = false);
|
||||
}
|
||||
}
|
||||
|
||||
void _applyToAllLanguages() {
|
||||
if (_controllers.isEmpty) return;
|
||||
final firstLang = widget.newValues.first.language!;
|
||||
@ -130,7 +193,7 @@ class _TranslationInputContainerState extends State<TranslationInputContainer> {
|
||||
children: [
|
||||
...widget.newValues.map((t) => _buildLanguageSection(t)),
|
||||
const SizedBox(height: 8),
|
||||
if (widget.resourceTypes == null)
|
||||
if (widget.resourceTypes == null) ...[
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 370,
|
||||
@ -144,6 +207,26 @@ class _TranslationInputContainerState extends State<TranslationInputContainer> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Builder(builder: (ctx) {
|
||||
final instance = ctx.watch<AppContext>().getContext()?.instanceDTO;
|
||||
if (instance?.isAssistant != true) return const SizedBox.shrink();
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 370,
|
||||
height: 70,
|
||||
child: _isTranslating
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: RoundedButton(
|
||||
text: "Traduire via IA",
|
||||
icon: Icons.auto_awesome,
|
||||
color: kPrimaryColor,
|
||||
press: _translateWithAI,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
]
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ class ManagerAppContext with ChangeNotifier{
|
||||
SectionDTO? selectedSection;
|
||||
bool? isLoading = false;
|
||||
UserRole? role;
|
||||
Locale locale = const Locale('fr');
|
||||
|
||||
bool get canEdit => role != null && role!.value <= 2;
|
||||
|
||||
|
||||
@ -2,8 +2,10 @@ import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -61,6 +63,7 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
}
|
||||
|
||||
void _showCreateDialog(BuildContext context, ManagerAppContext ctx) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final nameCtrl = TextEditingController();
|
||||
ApiKeyAppType selectedType = ApiKeyAppType.number0;
|
||||
DateTime? selectedExpiration;
|
||||
@ -69,14 +72,14 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
context: context,
|
||||
builder: (_) => StatefulBuilder(builder: (ctx2, setLocal) {
|
||||
return AlertDialog(
|
||||
title: const Text('Créer une clé API'),
|
||||
title: Text(l.createApiKey),
|
||||
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
TextField(controller: nameCtrl, decoration: const InputDecoration(labelText: 'Nom')),
|
||||
TextField(controller: nameCtrl, decoration: InputDecoration(labelText: l.name)),
|
||||
const SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Type d\'application', style: TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
Text(l.appType, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
DropdownButton<ApiKeyAppType>(
|
||||
value: selectedType,
|
||||
isExpanded: true,
|
||||
@ -93,8 +96,8 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
Expanded(
|
||||
child: Text(
|
||||
selectedExpiration == null
|
||||
? 'Pas d\'expiration'
|
||||
: 'Expire le ${selectedExpiration!.day.toString().padLeft(2, '0')}/${selectedExpiration!.month.toString().padLeft(2, '0')}/${selectedExpiration!.year}',
|
||||
? l.noExpiration
|
||||
: l.expiresOn('${selectedExpiration!.day.toString().padLeft(2, '0')}/${selectedExpiration!.month.toString().padLeft(2, '0')}/${selectedExpiration!.year}'),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
@ -108,19 +111,19 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
);
|
||||
if (picked != null) setLocal(() => selectedExpiration = picked);
|
||||
},
|
||||
child: const Text('Choisir'),
|
||||
child: Text(l.choose),
|
||||
),
|
||||
if (selectedExpiration != null)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close, size: 16),
|
||||
onPressed: () => setLocal(() => selectedExpiration = null),
|
||||
tooltip: 'Supprimer l\'expiration',
|
||||
tooltip: l.removeExpiration,
|
||||
),
|
||||
],
|
||||
),
|
||||
]),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(ctx2, rootNavigator: true).pop(), child: const Text('Annuler')),
|
||||
TextButton(onPressed: () => Navigator.of(ctx2, rootNavigator: true).pop(), child: Text(l.cancel)),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(ctx2, rootNavigator: true).pop();
|
||||
@ -129,7 +132,7 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
_showPlainKeyDialog(context, plainKey);
|
||||
}
|
||||
},
|
||||
child: const Text('Créer'),
|
||||
child: Text(l.create),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -138,15 +141,16 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
}
|
||||
|
||||
void _showPlainKeyDialog(BuildContext context, String plainKey) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Clé API créée'),
|
||||
title: Text(l.apiKeyCreatedTitle),
|
||||
content: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
const Text(
|
||||
'Copiez cette clé maintenant — elle ne sera plus affichée.',
|
||||
style: TextStyle(color: Colors.orange, fontWeight: FontWeight.w600),
|
||||
Text(
|
||||
l.copyKeyWarning,
|
||||
style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
@ -160,7 +164,7 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
Expanded(child: SelectableText(plainKey, style: const TextStyle(fontFamily: 'monospace', fontSize: 13))),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy, size: 18),
|
||||
tooltip: 'Copier',
|
||||
tooltip: l.copy,
|
||||
onPressed: () => Clipboard.setData(ClipboardData(text: plainKey)),
|
||||
),
|
||||
]),
|
||||
@ -169,7 +173,7 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context, rootNavigator: true).pop(),
|
||||
child: const Text('J\'ai copié la clé'),
|
||||
child: Text(l.copiedKey),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -177,20 +181,21 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
}
|
||||
|
||||
void _confirmRevoke(BuildContext context, ManagerAppContext ctx, Map<String, dynamic> key) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: const Text('Révoquer la clé API'),
|
||||
content: Text('Révoquer « ${key['name']} » ? Les apps utilisant cette clé perdront l\'accès.'),
|
||||
title: Text(l.revokeApiKeyTitle),
|
||||
content: Text(l.revokeApiKeyConfirm(key['name'] as String? ?? '')),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Annuler')),
|
||||
TextButton(onPressed: () => Navigator.pop(dialogContext), child: Text(l.cancel)),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||||
onPressed: () async {
|
||||
Navigator.pop(dialogContext);
|
||||
await _revokeKey(ctx, key['id'] as String);
|
||||
},
|
||||
child: const Text('Révoquer', style: TextStyle(color: Colors.white)),
|
||||
child: Text(l.revoke, style: const TextStyle(color: Colors.white)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -208,6 +213,7 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final appContext = Provider.of<AppContext>(context);
|
||||
final managerCtx = appContext.getContext() as ManagerAppContext;
|
||||
|
||||
@ -217,19 +223,19 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Clés API', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
Text(l.apiKeysTitle, style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _showCreateDialog(context, managerCtx),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Créer une clé'),
|
||||
label: Text(l.createKeyBtn),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_loading)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
const CommonLoader()
|
||||
else if (_keys.isEmpty)
|
||||
const Center(child: Text('Aucune clé API'))
|
||||
Center(child: Text(l.noApiKeys))
|
||||
else
|
||||
Expanded(
|
||||
child: Card(
|
||||
@ -248,13 +254,13 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
columnSpacing: 24,
|
||||
headingRowColor: WidgetStateProperty.all(Colors.grey.shade50),
|
||||
dividerThickness: 1,
|
||||
columns: const [
|
||||
DataColumn(label: Text('Nom', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Type', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Créée le', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Expiration', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Statut', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Actions', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
columns: [
|
||||
DataColumn(label: Text(l.name, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.type, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.createdOn, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.expiration, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.status, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.actions, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
],
|
||||
rows: _keys.map((key) {
|
||||
final isActive = key['isActive'] as bool? ?? false;
|
||||
@ -276,7 +282,7 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
DataCell(Text(expStr, style: TextStyle(color: exp != null && exp.isBefore(DateTime.now()) ? Colors.red : Colors.grey))),
|
||||
DataCell(Chip(
|
||||
label: Text(
|
||||
isActive ? 'Active' : 'Révoquée',
|
||||
isActive ? l.activeKey : l.revokedKey,
|
||||
style: TextStyle(
|
||||
color: isActive ? Colors.green.shade700 : Colors.red.shade700,
|
||||
fontSize: 12,
|
||||
@ -290,7 +296,7 @@ class _ApiKeysScreenState extends State<ApiKeysScreen> {
|
||||
DataCell(isActive
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.block, color: Colors.red, size: 20),
|
||||
tooltip: 'Révoquer',
|
||||
tooltip: l.tooltipRevoke,
|
||||
onPressed: () => _confirmRevoke(context, managerCtx, key),
|
||||
)
|
||||
: const SizedBox()),
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:manager_app/Components/string_input_container.dart';
|
||||
import 'package:manager_app/Screens/Resources/resources_screen.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
import '../Kiosk_devices/change_device_info_modal.dart';
|
||||
@ -19,7 +20,7 @@ dynamic showAddConfigurationLink (BuildContext mainContext, AppContext appContex
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20.0))
|
||||
),
|
||||
title: Center(child: Text("Sélectionner une configuration à ajouter")),
|
||||
title: Center(child: Text(AppLocalizations.of(context)!.selectConfigToAdd)),
|
||||
content: FutureBuilder(
|
||||
future: getConfigurations(appContext),
|
||||
builder: (context, AsyncSnapshot<dynamic> snapshot) {
|
||||
@ -35,7 +36,7 @@ dynamic showAddConfigurationLink (BuildContext mainContext, AppContext appContex
|
||||
height: size.height * 0.5,
|
||||
width: size.width * 0.5,
|
||||
constraints: const BoxConstraints(minHeight: 250, minWidth: 250),
|
||||
child: configurations.length == 0 ? Center(child: Text("Aucun élément à afficher")) : ListView.builder(
|
||||
child: configurations.length == 0 ? Center(child: Text(AppLocalizations.of(context)!.noItems)) : ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: configurations.length,
|
||||
itemBuilder: (context, index) {
|
||||
@ -115,7 +116,7 @@ dynamic showAddConfigurationLink (BuildContext mainContext, AppContext appContex
|
||||
width: 180,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Annuler",
|
||||
text: AppLocalizations.of(context)!.cancel,
|
||||
icon: Icons.undo,
|
||||
color: kSecond,
|
||||
press: () {
|
||||
@ -128,7 +129,7 @@ dynamic showAddConfigurationLink (BuildContext mainContext, AppContext appContex
|
||||
width: 180,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Ajouter",
|
||||
text: AppLocalizations.of(context)!.add,
|
||||
icon: Icons.add,
|
||||
color: kPrimaryColor,
|
||||
press: () {
|
||||
|
||||
@ -17,6 +17,7 @@ import 'package:manager_app/Screens/Applications/add_configuration_link_popup.da
|
||||
import 'package:manager_app/Screens/Applications/phone_mockup.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -57,7 +58,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Informations générales", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)),
|
||||
Text(AppLocalizations.of(context)!.generalInfo, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)),
|
||||
SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: Center(
|
||||
@ -75,7 +76,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
height: elementHeight,
|
||||
child: Center(
|
||||
child: ResourceInputContainer(
|
||||
label: "Image principale :",
|
||||
label: AppLocalizations.of(context)!.mainImageLabel,
|
||||
initialValue: _applicationInstanceDTO.mainImageId,
|
||||
color: kPrimaryColor,
|
||||
imageFit: BoxFit.fitHeight,
|
||||
@ -91,7 +92,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
// automatic save
|
||||
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
|
||||
if(applicationLink != null) {
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
/*setState(() {
|
||||
|
||||
});*/
|
||||
@ -106,7 +107,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
height: elementHeight,
|
||||
child: Center(
|
||||
child: ResourceInputContainer(
|
||||
label: "Loader :",
|
||||
label: AppLocalizations.of(context)!.loaderLabel,
|
||||
initialValue: _applicationInstanceDTO.loaderImageId,
|
||||
color: kPrimaryColor,
|
||||
imageFit: BoxFit.fitHeight,
|
||||
@ -122,7 +123,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
// automatic save
|
||||
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
|
||||
if(applicationLink != null) {
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
/*setState(() {
|
||||
|
||||
});*/
|
||||
@ -137,7 +138,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
height: elementHeight,
|
||||
child: Center(
|
||||
child: ColorPickerInputContainer(
|
||||
label: "Couleur principale :",
|
||||
label: AppLocalizations.of(context)!.primaryColorLabel,
|
||||
fontSize: 20,
|
||||
color: _applicationInstanceDTO.primaryColor,
|
||||
onChanged: (value) async {
|
||||
@ -145,7 +146,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
// automatic save
|
||||
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
|
||||
if(applicationLink != null) {
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
/*setState(() {
|
||||
|
||||
});*/
|
||||
@ -160,7 +161,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
height: elementHeight,
|
||||
child: Center(
|
||||
child: ColorPickerInputContainer(
|
||||
label: "Couleur secondaire :",
|
||||
label: AppLocalizations.of(context)!.secondaryColorLabel,
|
||||
fontSize: 20,
|
||||
color: _applicationInstanceDTO.secondaryColor,
|
||||
onChanged: (value) async {
|
||||
@ -168,7 +169,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
// automatic save
|
||||
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
|
||||
if(applicationLink != null) {
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
/*setState(() {
|
||||
|
||||
});*/
|
||||
@ -183,17 +184,17 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
height: elementHeight,
|
||||
child: Center(
|
||||
child: SegmentedEnumInputContainer(
|
||||
label: "Affichage :",
|
||||
label: AppLocalizations.of(context)!.layoutLabel,
|
||||
selected: _applicationInstanceDTO.layoutMainPage,
|
||||
values: LayoutMainPageType.values,
|
||||
inputValues: { LayoutMainPageType.SimpleGrid: {'label': 'Grille', 'icon': Icons.grid_view}, LayoutMainPageType.MasonryGrid : {'label': 'Masonry', 'icon': Icons.view_quilt }},
|
||||
inputValues: { LayoutMainPageType.SimpleGrid: {'label': AppLocalizations.of(context)!.layoutGrid, 'icon': Icons.grid_view}, LayoutMainPageType.MasonryGrid : {'label': 'Masonry', 'icon': Icons.view_quilt }},
|
||||
onChanged: (value) async {
|
||||
var tempOutput = value;
|
||||
_applicationInstanceDTO.layoutMainPage = tempOutput;
|
||||
// automatic save
|
||||
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
|
||||
if(applicationLink != null) {
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
/*setState(() {
|
||||
|
||||
});*/
|
||||
@ -209,7 +210,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
height: elementHeight,
|
||||
child: Center(
|
||||
child: MultiSelectDropdownLanguageContainer(
|
||||
label: "Langues :",
|
||||
label: AppLocalizations.of(context)!.languagesLabel,
|
||||
initialValue: _applicationInstanceDTO.languages != null ? _applicationInstanceDTO.languages!: [],
|
||||
values: languages,
|
||||
isMultiple: true,
|
||||
@ -221,7 +222,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
// automatic save
|
||||
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
|
||||
if(applicationLink != null) {
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
/*setState(() {
|
||||
|
||||
});*/
|
||||
@ -245,14 +246,14 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
List<SectionEventDTO>? sectionEvents = rawSubsections?.whereType<SectionEventDTO>().toList();
|
||||
sectionEvents = sectionEvents == null ? [] : sectionEvents;
|
||||
|
||||
sectionEvents.add(SectionEventDTO(id: null, label: "Aucun"));
|
||||
sectionEvents.add(SectionEventDTO(id: null, label: AppLocalizations.of(context)!.noneOption));
|
||||
|
||||
print(_applicationInstanceDTO.sectionEventId);
|
||||
print(_applicationInstanceDTO.sectionEventDTO);
|
||||
|
||||
return SingleChoiceInputContainer<SectionEventDTO?>(
|
||||
label: "Evènement à l'affiche :",
|
||||
selectLabel: "Choisir un évènement",
|
||||
label: AppLocalizations.of(context)!.featuredEventLabel,
|
||||
selectLabel: AppLocalizations.of(context)!.chooseEvent,
|
||||
selected: _applicationInstanceDTO.sectionEventDTO,
|
||||
values: sectionEvents.toList(),
|
||||
valueExtractor: (SectionEventDTO? dto) => dto?.id ?? "",
|
||||
@ -273,7 +274,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
// automatic save
|
||||
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
|
||||
if(applicationLink != null) {
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
//setState(() {
|
||||
_applicationInstanceDTO.sectionEventDTO = applicationLink.sectionEventDTO;
|
||||
//_applicationInstanceDTO = applicationLink;
|
||||
@ -296,7 +297,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Assistant IA :", style: TextStyle(fontSize: 16)),
|
||||
Text(AppLocalizations.of(context)!.aiAssistantLabel, style: TextStyle(fontSize: 16)),
|
||||
Switch(
|
||||
activeThumbColor: kPrimaryColor,
|
||||
inactiveThumbColor: kBodyTextColor,
|
||||
@ -311,10 +312,10 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
localSetState(() {
|
||||
_applicationInstanceDTO.isAssistant = applicationLink.isAssistant;
|
||||
});
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification(kError, kWhite, "Une erreur est survenue", context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.errorOccurred, context, null);
|
||||
}
|
||||
},
|
||||
),
|
||||
@ -347,7 +348,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text("Configurations sur le téléphone", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)),
|
||||
child: Text(AppLocalizations.of(context)!.phoneConfigTitle, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 21)),
|
||||
),
|
||||
appConfigurationLinks != null ? Padding(
|
||||
padding: const EdgeInsets.only(left: 32, right: 32, top: 75),
|
||||
@ -375,7 +376,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
// TODO use order put method
|
||||
var result = await updateAppConfigurationOrder(appContext, updatedList);
|
||||
localSetState(() {});
|
||||
showNotification(kSuccess, kWhite, "Application mobile mise à jour succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.appUpdatedSuccess, context, null);
|
||||
},
|
||||
actions: [
|
||||
(BuildContext context, int index, AppConfigurationLinkDTO link) {
|
||||
@ -394,16 +395,16 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
var applicationLink = await updateApplicationLink(appContext, link);
|
||||
if(applicationLink != null) {
|
||||
if(newValue) {
|
||||
showNotification(kSuccess, kWhite, "Configuration activée avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configActivatedSuccess, context, null);
|
||||
} else {
|
||||
showNotification(kSuccess, kWhite, "Configuration désactivée avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configDeactivatedSuccess, context, null);
|
||||
}
|
||||
localSetState(() {
|
||||
link.isActive = applicationLink.isActive;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification(kError, kWhite, "Une erreur est survenue", context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.errorOccurred, context, null);
|
||||
}
|
||||
},
|
||||
),
|
||||
@ -416,19 +417,19 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir retirer cette configuration de l'application ?",
|
||||
AppLocalizations.of(context)!.configRemoveConfirm,
|
||||
() {},
|
||||
() async {
|
||||
try {
|
||||
var result = await deleteConfigurationToApp(appContext, link, _applicationInstanceDTO);
|
||||
|
||||
showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configRemovedSuccess, context, null);
|
||||
|
||||
setState(() {
|
||||
// for refresh ui
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors du retrait de la configuration', context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.configRemoveError, context, null);
|
||||
}
|
||||
},
|
||||
context
|
||||
@ -498,19 +499,19 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir retirer cette configuration de l'application ?",
|
||||
AppLocalizations.of(context)!.configRemoveConfirm,
|
||||
() {},
|
||||
() async {
|
||||
try {
|
||||
var result = await deleteConfigurationToApp(appContext, appConfigurationLink, _applicationInstanceDTO);
|
||||
|
||||
showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configRemovedSuccess, context, null);
|
||||
|
||||
setState(() {
|
||||
// for refresh ui
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors du retrait de la configuration', context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.configRemoveError, context, null);
|
||||
}
|
||||
},
|
||||
context
|
||||
@ -526,7 +527,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
),
|
||||
),
|
||||
),*/
|
||||
): Center(child: Text("No data")),
|
||||
): Center(child: Text(AppLocalizations.of(context)!.noData)),
|
||||
appConfigurationLinks != null ? Positioned(
|
||||
top: 8,
|
||||
right: 8,
|
||||
@ -630,7 +631,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text("Informations générales"),
|
||||
child: Text(AppLocalizations.of(context)!.generalInfo),
|
||||
)
|
||||
),
|
||||
Align(
|
||||
@ -656,7 +657,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
),*/
|
||||
// Image principale
|
||||
ResourceInputContainer(
|
||||
label: "Image principale :",
|
||||
label: AppLocalizations.of(context)!.mainImageLabel,
|
||||
initialValue: _applicationInstanceDTO.mainImageId,
|
||||
color: kPrimaryColor,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
@ -671,7 +672,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
),
|
||||
// Image Loader
|
||||
ResourceInputContainer(
|
||||
label: "Loader :",
|
||||
label: AppLocalizations.of(context)!.loaderLabel,
|
||||
initialValue: _applicationInstanceDTO.loaderImageId,
|
||||
color: kPrimaryColor,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
@ -686,7 +687,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
),
|
||||
// Primary color
|
||||
ColorPickerInputContainer(
|
||||
label: "Couleur principale :",
|
||||
label: AppLocalizations.of(context)!.primaryColorLabel,
|
||||
fontSize: 20,
|
||||
color: _applicationInstanceDTO.primaryColor,
|
||||
onChanged: (value) {
|
||||
@ -695,7 +696,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
),
|
||||
// Secondary color
|
||||
ColorPickerInputContainer(
|
||||
label: "Couleur secondaire :",
|
||||
label: AppLocalizations.of(context)!.secondaryColorLabel,
|
||||
fontSize: 20,
|
||||
color: _applicationInstanceDTO.secondaryColor,
|
||||
onChanged: (value) {
|
||||
@ -706,7 +707,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
Text('Todo Type selector Grid or Mansonry'),
|
||||
// Langues
|
||||
MultiSelectDropdownLanguageContainer(
|
||||
label: "Langues :",
|
||||
label: AppLocalizations.of(context)!.languagesLabel,
|
||||
initialValue: _applicationInstanceDTO.languages != null ? _applicationInstanceDTO.languages!: [],
|
||||
values: languages,
|
||||
isMultiple: true,
|
||||
@ -728,7 +729,7 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text("Configurations sur le téléphone"),
|
||||
child: Text(AppLocalizations.of(context)!.phoneConfigTitle),
|
||||
)
|
||||
),
|
||||
Align(
|
||||
@ -774,16 +775,16 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
var applicationLink = await updateApplicationInstance(appContext, _applicationInstanceDTO);
|
||||
if(applicationLink != null) {
|
||||
if(newValue) {
|
||||
showNotification(kSuccess, kWhite, "Configuration activée avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configActivatedSuccess, context, null);
|
||||
} else {
|
||||
showNotification(kSuccess, kWhite, "Configuration désactivée avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configDeactivatedSuccess, context, null);
|
||||
}
|
||||
setState(() {
|
||||
link.isActive = applicationLink.isActive;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification(kError, kWhite, "Une erreur est survenue", context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.errorOccurred, context, null);
|
||||
}
|
||||
},
|
||||
child: Icon(Icons.star, color: kError, size: 25),
|
||||
@ -806,16 +807,16 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
var applicationLink = await updateApplicationLink(appContext, link);
|
||||
if(applicationLink != null) {
|
||||
if(newValue) {
|
||||
showNotification(kSuccess, kWhite, "Configuration activée avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configActivatedSuccess, context, null);
|
||||
} else {
|
||||
showNotification(kSuccess, kWhite, "Configuration désactivée avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configDeactivatedSuccess, context, null);
|
||||
}
|
||||
setState(() {
|
||||
link.isActive = applicationLink.isActive;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification(kError, kWhite, "Une erreur est survenue", context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.errorOccurred, context, null);
|
||||
}
|
||||
},
|
||||
),
|
||||
@ -828,19 +829,19 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir retirer cette configuration de l'application ?",
|
||||
AppLocalizations.of(context)!.configRemoveConfirm,
|
||||
() {},
|
||||
() async {
|
||||
try {
|
||||
var result = await deleteConfigurationToApp(appContext, link, _applicationInstanceDTO);
|
||||
|
||||
showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configRemovedSuccess, context, null);
|
||||
|
||||
setState(() {
|
||||
// for refresh ui
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors du retrait de la configuration', context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.configRemoveError, context, null);
|
||||
}
|
||||
},
|
||||
context
|
||||
@ -949,19 +950,19 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir retirer cette configuration de l'application ?",
|
||||
AppLocalizations.of(context)!.configRemoveConfirm,
|
||||
() {},
|
||||
() async {
|
||||
try {
|
||||
var result = await deleteConfigurationToApp(appContext, appConfigurationLink, _applicationInstanceDTO);
|
||||
|
||||
showNotification(kSuccess, kWhite, "La configuration a été retirée de l'application avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configRemovedSuccess, context, null);
|
||||
|
||||
setState(() {
|
||||
// for refresh ui
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors du retrait de la configuration', context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.configRemoveError, context, null);
|
||||
}
|
||||
},
|
||||
context
|
||||
@ -979,14 +980,14 @@ class _AppConfigurationLinkScreenState extends State<AppConfigurationLinkScreen>
|
||||
),*/
|
||||
],
|
||||
),
|
||||
): Center(child: Text("No data")),
|
||||
): Center(child: Text(AppLocalizations.of(context)!.noData)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (snapshot.connectionState == ConnectionState.none) {
|
||||
return Text("No data");
|
||||
return Text(AppLocalizations.of(context)!.noData);
|
||||
} else {
|
||||
return Center(
|
||||
child: Container(
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:manager_app/Components/single_select_container.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'showNewOrUpdateEventAgenda.dart';
|
||||
@ -94,12 +95,12 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text("Évènements",
|
||||
Text(AppLocalizations.of(context)!.agendaEventsLabel,
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text("Ajouter un évènement"),
|
||||
label: Text(AppLocalizations.of(context)!.addEvent),
|
||||
onPressed: agendaDTO.id == null
|
||||
? null
|
||||
: () {
|
||||
@ -115,13 +116,13 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
if (created != null && mounted) {
|
||||
setState(() => events.add(created));
|
||||
showNotification(kSuccess, kWhite,
|
||||
'Évènement créé avec succès', context, null);
|
||||
AppLocalizations.of(context)!.agendaEventCreatedSuccess, context, null);
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la création de l\'évènement',
|
||||
AppLocalizations.of(context)!.agendaEventCreateError,
|
||||
context,
|
||||
null);
|
||||
rethrow;
|
||||
@ -145,8 +146,8 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
height: 600,
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: events.isEmpty
|
||||
? const Center(
|
||||
child: Text("Aucun évènement",
|
||||
? Center(
|
||||
child: Text(AppLocalizations.of(context)!.noEvents,
|
||||
style: TextStyle(fontStyle: FontStyle.italic)))
|
||||
: ListView.builder(
|
||||
itemCount: events.length,
|
||||
@ -170,14 +171,14 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
(event.label != null && event.label!.isNotEmpty)
|
||||
? (event.label!.firstWhere(
|
||||
(t) => t.language == 'FR',
|
||||
orElse: () => event.label![0])).value ?? "Évènement $index"
|
||||
: "Évènement $index",
|
||||
orElse: () => event.label![0])).value ?? "${AppLocalizations.of(context)!.agendaEventFallback} $index"
|
||||
: "${AppLocalizations.of(context)!.agendaEventFallback} $index",
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child:
|
||||
Text(event.address?.address ?? "Pas d'adresse"),
|
||||
Text(event.address?.address ?? AppLocalizations.of(context)!.noAddress),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -201,7 +202,7 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Évènement mis à jour avec succès',
|
||||
AppLocalizations.of(context)!.agendaEventUpdatedSuccess,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -209,7 +210,7 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la mise à jour de l\'évènement',
|
||||
AppLocalizations.of(context)!.agendaEventUpdateError,
|
||||
context,
|
||||
null);
|
||||
rethrow;
|
||||
@ -232,7 +233,7 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Évènement supprimé avec succès',
|
||||
AppLocalizations.of(context)!.agendaEventDeletedSuccess,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -240,7 +241,7 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la suppression de l\'évènement',
|
||||
AppLocalizations.of(context)!.agendaEventDeleteError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -279,7 +280,7 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
CheckInputContainer(
|
||||
label: "En ligne :",
|
||||
label: AppLocalizations.of(context)!.onlineLabel,
|
||||
isChecked: agendaDTO.isOnlineAgenda ?? true,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
@ -289,7 +290,7 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
},
|
||||
),
|
||||
CheckInputContainer(
|
||||
label: "Vue carte :",
|
||||
label: AppLocalizations.of(context)!.mapViewLabel,
|
||||
isChecked: agendaDTO.agendaMapProvider != null,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
@ -304,7 +305,7 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
),
|
||||
if (agendaDTO.agendaMapProvider != null)
|
||||
SingleSelectContainer(
|
||||
label: "Service carte :",
|
||||
label: AppLocalizations.of(context)!.mapServiceLabel,
|
||||
color: Colors.black,
|
||||
initialValue: mapProviderIn,
|
||||
inputValues: const ["Google", "MapBox"],
|
||||
@ -324,9 +325,9 @@ class _AgendaConfigState extends State<AgendaConfig> {
|
||||
),
|
||||
if (agendaDTO.isOnlineAgenda == true)
|
||||
MultiStringInputContainer(
|
||||
label: "Fichiers json :",
|
||||
label: AppLocalizations.of(context)!.jsonFilesLabel,
|
||||
resourceTypes: const [ResourceType.Json, ResourceType.JsonUrl],
|
||||
modalLabel: "JSON",
|
||||
modalLabel: AppLocalizations.of(context)!.jsonLabel,
|
||||
color: kPrimaryColor,
|
||||
initialValue: agendaDTO.resourceIds ?? [],
|
||||
isTitle: false,
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Components/multi_string_input_container.dart';
|
||||
import 'package:manager_app/Components/string_input_container.dart';
|
||||
@ -51,7 +52,7 @@ void showNewOrUpdateEventAgenda(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
event == null ? "Nouvel Évènement" : "Modifier l'Évènement",
|
||||
event == null ? AppLocalizations.of(context)!.newAgendaEventTitle : AppLocalizations.of(context)!.editAgendaEventTitle,
|
||||
style: TextStyle(
|
||||
color: kPrimaryColor,
|
||||
fontSize: 20,
|
||||
@ -70,7 +71,7 @@ void showNewOrUpdateEventAgenda(
|
||||
width: halfWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Titre :",
|
||||
modalLabel: "Titre de l'évènement",
|
||||
modalLabel: AppLocalizations.of(context)!.eventTitleLabel,
|
||||
initialValue: workingEvent.label ?? [],
|
||||
onGetResult: (val) =>
|
||||
setState(() => workingEvent.label = val),
|
||||
@ -84,7 +85,7 @@ void showNewOrUpdateEventAgenda(
|
||||
width: halfWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Description :",
|
||||
modalLabel: "Description de l'évènement",
|
||||
modalLabel: AppLocalizations.of(context)!.eventDescriptionLabel,
|
||||
initialValue: workingEvent.description ?? [],
|
||||
onGetResult: (val) => setState(
|
||||
() => workingEvent.description = val),
|
||||
@ -104,7 +105,7 @@ void showNewOrUpdateEventAgenda(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Date de début :"),
|
||||
Text(AppLocalizations.of(context)!.startDateColonLabel),
|
||||
SizedBox(height: 4),
|
||||
OutlinedButton.icon(
|
||||
icon: Icon(Icons.calendar_today, size: 16),
|
||||
@ -172,7 +173,7 @@ void showNewOrUpdateEventAgenda(
|
||||
SizedBox(
|
||||
width: halfWidth,
|
||||
child: ResourceInputContainer(
|
||||
label: "Vidéo :",
|
||||
label: AppLocalizations.of(context)!.videoResourceLabel,
|
||||
initialValue: workingEvent.videoResourceId,
|
||||
inResourceTypes: const [ResourceType.Video, ResourceType.VideoUrl],
|
||||
onChanged: (res) => setState(() {
|
||||
@ -199,7 +200,7 @@ void showNewOrUpdateEventAgenda(
|
||||
SizedBox(
|
||||
width: halfWidth,
|
||||
child: StringInputContainer(
|
||||
label: "Lien vidéo direct :",
|
||||
label: AppLocalizations.of(context)!.directVideoLinkLabel,
|
||||
initialValue: workingEvent.videoLink ?? "",
|
||||
onChanged: (val) => setState(() => workingEvent.videoLink = val.isEmpty ? null : val),
|
||||
),
|
||||
@ -223,7 +224,7 @@ void showNewOrUpdateEventAgenda(
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: StringInputContainer(
|
||||
label: "Téléphone :",
|
||||
label: AppLocalizations.of(context)!.phoneLabel,
|
||||
initialValue: workingEvent.phone ?? "",
|
||||
onChanged: (val) =>
|
||||
setState(() => workingEvent.phone = val),
|
||||
@ -242,7 +243,7 @@ void showNewOrUpdateEventAgenda(
|
||||
],
|
||||
),
|
||||
Divider(height: 24),
|
||||
Text("Localisation / Géométrie",
|
||||
Text(AppLocalizations.of(context)!.locationGeometryLabel,
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
SizedBox(height: 8),
|
||||
StringInputContainer(
|
||||
|
||||
@ -8,6 +8,7 @@ import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
||||
@ -79,7 +80,7 @@ class _ArticleConfigState extends State<ArticleConfig> {
|
||||
Column(
|
||||
children: [
|
||||
MultiStringInputContainer(
|
||||
label: "Contenu affiché :",
|
||||
label: AppLocalizations.of(context)!.displayedContentLabel,
|
||||
modalLabel: "Contenu",
|
||||
color: kPrimaryColor,
|
||||
isHTML: true,
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:intl/intl.dart';
|
||||
import 'showNewOrUpdateProgrammeBlock.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Parcours/parcours_config.dart';
|
||||
@ -98,11 +99,11 @@ class _EventConfigState extends State<EventConfig> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListTile(
|
||||
title: Text("Date de début"),
|
||||
title: Text(AppLocalizations.of(context)!.startDateLabel),
|
||||
subtitle: Text(eventDTO.startDate != null
|
||||
? DateFormat('dd/MM/yyyy HH:mm')
|
||||
.format(eventDTO.startDate!.toLocal())
|
||||
: "Non définie"),
|
||||
: AppLocalizations.of(context)!.notDefined),
|
||||
trailing: Icon(Icons.calendar_today),
|
||||
onTap: () async {
|
||||
DateTime initialDate = eventDTO.startDate?.toLocal() ?? DateTime.now();
|
||||
@ -158,10 +159,10 @@ class _EventConfigState extends State<EventConfig> {
|
||||
),
|
||||
Expanded(
|
||||
child: ListTile(
|
||||
title: Text("Date de fin"),
|
||||
title: Text(AppLocalizations.of(context)!.endDateLabel),
|
||||
subtitle: Text(eventDTO.endDate != null
|
||||
? DateFormat('dd/MM/yyyy HH:mm').format(eventDTO.endDate!.toLocal())
|
||||
: "Non définie"),
|
||||
: AppLocalizations.of(context)!.notDefined),
|
||||
trailing: Icon(Icons.calendar_today),
|
||||
onTap: () async {
|
||||
DateTime initialDate = eventDTO.endDate?.toLocal() ??
|
||||
@ -231,11 +232,11 @@ class _EventConfigState extends State<EventConfig> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Programme",
|
||||
Text(AppLocalizations.of(context)!.programmeLabel,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
ElevatedButton.icon(
|
||||
icon: Icon(Icons.add),
|
||||
label: Text("Ajouter un bloc"),
|
||||
label: Text(AppLocalizations.of(context)!.addBlock),
|
||||
onPressed: () {
|
||||
final appContext =
|
||||
Provider.of<AppContext>(context, listen: false);
|
||||
@ -277,7 +278,7 @@ class _EventConfigState extends State<EventConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Bloc de programme créé avec succès',
|
||||
AppLocalizations.of(context)!.programmeBlockCreatedSuccess,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -285,7 +286,7 @@ class _EventConfigState extends State<EventConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la création du bloc',
|
||||
AppLocalizations.of(context)!.programmeBlockCreateError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -302,7 +303,7 @@ class _EventConfigState extends State<EventConfig> {
|
||||
height: 600,
|
||||
child: (eventDTO.programme == null || eventDTO.programme!.isEmpty)
|
||||
? Center(
|
||||
child: Text("Aucun bloc de programme défini",
|
||||
child: Text(AppLocalizations.of(context)!.noBlocks,
|
||||
style: TextStyle(fontStyle: FontStyle.italic)))
|
||||
: ListView.builder(
|
||||
itemCount: eventDTO.programme!.length,
|
||||
@ -318,9 +319,9 @@ class _EventConfigState extends State<EventConfig> {
|
||||
(t) => t.language == 'FR',
|
||||
orElse: () => block.title![0])
|
||||
.value ??
|
||||
"Bloc ${index + 1}",
|
||||
"${AppLocalizations.of(context)!.blockFallback} ${index + 1}",
|
||||
)
|
||||
: Text("Bloc ${index + 1}"),
|
||||
: Text("${AppLocalizations.of(context)!.blockFallback} ${index + 1}"),
|
||||
subtitle: Text(
|
||||
"${block.startTime != null ? DateFormat('HH:mm').format(block.startTime!.toLocal()) : '??'} - ${block.endTime != null ? DateFormat('HH:mm').format(block.endTime!.toLocal()) : '??'}"),
|
||||
trailing: Row(
|
||||
@ -368,7 +369,7 @@ class _EventConfigState extends State<EventConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Bloc mis à jour avec succès',
|
||||
AppLocalizations.of(context)!.programmeBlockUpdatedSuccess,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -376,7 +377,7 @@ class _EventConfigState extends State<EventConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la mise à jour du bloc',
|
||||
AppLocalizations.of(context)!.programmeBlockUpdateError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -407,14 +408,14 @@ class _EventConfigState extends State<EventConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Bloc supprimé avec succès',
|
||||
AppLocalizations.of(context)!.programmeBlockDeletedSuccess,
|
||||
context,
|
||||
null);
|
||||
} catch (e) {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la suppression du bloc',
|
||||
AppLocalizations.of(context)!.programmeBlockDeleteError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -457,10 +458,10 @@ class _EventConfigState extends State<EventConfig> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Carte de base",
|
||||
Text(AppLocalizations.of(context)!.baseMapLabel,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
DropDownInputContainer(
|
||||
label: "Carte :",
|
||||
label: AppLocalizations.of(context)!.mapLabel,
|
||||
values: mapItems,
|
||||
initialValue: currentValue,
|
||||
onChange: (val) {
|
||||
@ -493,11 +494,11 @@ class _EventConfigState extends State<EventConfig> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text("Annotations globales",
|
||||
Text(AppLocalizations.of(context)!.globalAnnotationsLabel,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text("Ajouter une annotation"),
|
||||
label: Text(AppLocalizations.of(context)!.addAnnotation),
|
||||
onPressed: eventDTO.id == null
|
||||
? null
|
||||
: () {
|
||||
@ -518,11 +519,11 @@ class _EventConfigState extends State<EventConfig> {
|
||||
];
|
||||
});
|
||||
showNotification(kSuccess, kWhite,
|
||||
'Annotation créée avec succès', context, null);
|
||||
AppLocalizations.of(context)!.annotationCreatedSuccess, context, null);
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification(kError, kWhite,
|
||||
'Erreur lors de la création de l\'annotation',
|
||||
AppLocalizations.of(context)!.annotationCreateError,
|
||||
context, null);
|
||||
}
|
||||
});
|
||||
@ -540,9 +541,9 @@ class _EventConfigState extends State<EventConfig> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (annotations.isEmpty)
|
||||
const Padding(
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Text("Aucune annotation globale",
|
||||
child: Text(AppLocalizations.of(context)!.noAnnotations,
|
||||
style: TextStyle(fontStyle: FontStyle.italic)),
|
||||
)
|
||||
else
|
||||
@ -552,8 +553,8 @@ class _EventConfigState extends State<EventConfig> {
|
||||
final labelText = ann.label != null && ann.label!.isNotEmpty
|
||||
? (ann.label!.firstWhere((t) => t.language == 'FR',
|
||||
orElse: () => ann.label![0])
|
||||
.value ?? 'Annotation ${idx + 1}')
|
||||
: 'Annotation ${idx + 1}';
|
||||
.value ?? '${AppLocalizations.of(context)!.annotationFallback} ${idx + 1}')
|
||||
: '${AppLocalizations.of(context)!.annotationFallback} ${idx + 1}';
|
||||
return Card(
|
||||
margin:
|
||||
const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
|
||||
@ -578,11 +579,11 @@ class _EventConfigState extends State<EventConfig> {
|
||||
eventDTO.globalMapAnnotations = updated;
|
||||
});
|
||||
showNotification(kSuccess, kWhite,
|
||||
'Annotation supprimée', context, null);
|
||||
AppLocalizations.of(context)!.annotationDeletedSuccess, context, null);
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification(kError, kWhite,
|
||||
'Erreur lors de la suppression', context, null);
|
||||
AppLocalizations.of(context)!.annotationDeleteError, context, null);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Components/multi_string_input_container.dart';
|
||||
import 'package:manager_app/Components/string_input_container.dart';
|
||||
@ -102,7 +103,7 @@ void showNewOrUpdateMapAnnotation(
|
||||
SizedBox(
|
||||
width: halfWidth,
|
||||
child: DropDownInputContainer(
|
||||
label: "Géométrie :",
|
||||
label: AppLocalizations.of(context)!.geometryLabel,
|
||||
values: geometryTypeLabels,
|
||||
initialValue: geometryTypeToLabel(working.geometryType),
|
||||
onChange: (val) => setState(() =>
|
||||
@ -127,7 +128,7 @@ void showNewOrUpdateMapAnnotation(
|
||||
SizedBox(
|
||||
width: halfWidth,
|
||||
child: StringInputContainer(
|
||||
label: "Icône (material) :",
|
||||
label: AppLocalizations.of(context)!.materialIconLabel,
|
||||
initialValue: working.icon ?? '',
|
||||
onChanged: (val) =>
|
||||
setState(() => working.icon = val.isEmpty ? null : val),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/confirmation_dialog.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Components/multi_string_input_container.dart';
|
||||
@ -112,12 +113,12 @@ void showNewOrUpdateProgrammeBlock(
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.schedule,
|
||||
color: kPrimaryColor),
|
||||
title: Text("Heure de début"),
|
||||
title: Text(AppLocalizations.of(context)!.startTimeLabel),
|
||||
subtitle: Text(
|
||||
workingBlock.startTime != null
|
||||
? DateFormat('HH:mm')
|
||||
.format(workingBlock.startTime!.toLocal())
|
||||
: "Non définie",
|
||||
: AppLocalizations.of(context)!.notDefined,
|
||||
style: TextStyle(
|
||||
color: kPrimaryColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
@ -161,12 +162,12 @@ void showNewOrUpdateProgrammeBlock(
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.schedule_outlined,
|
||||
color: kPrimaryColor),
|
||||
title: Text("Heure de fin"),
|
||||
title: Text(AppLocalizations.of(context)!.endTimeLabel),
|
||||
subtitle: Text(
|
||||
workingBlock.endTime != null
|
||||
? DateFormat('HH:mm')
|
||||
.format(workingBlock.endTime!.toLocal())
|
||||
: "Non définie",
|
||||
: AppLocalizations.of(context)!.notDefined,
|
||||
style: TextStyle(
|
||||
color: kPrimaryColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
@ -212,7 +213,7 @@ void showNewOrUpdateProgrammeBlock(
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Annotations",
|
||||
Text(AppLocalizations.of(context)!.annotationsLabel,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 15)),
|
||||
@ -242,7 +243,7 @@ void showNewOrUpdateProgrammeBlock(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(
|
||||
"Aucune annotation configurée.",
|
||||
AppLocalizations.of(context)!.noAnnotationConfigured,
|
||||
style: TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Colors.grey[600]),
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:manager_app/Components/resource_input_container.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Parcours/parcours_config.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
|
||||
class GameConfig extends StatefulWidget {
|
||||
final String? color;
|
||||
@ -148,8 +149,8 @@ class _GameConfigState extends State<GameConfig> {
|
||||
),
|
||||
Flexible(
|
||||
child: MultiStringInputAndResourceContainer(
|
||||
label: "Message départ :",
|
||||
modalLabel: "Message départ",
|
||||
label: AppLocalizations.of(context)!.startMessageLabel,
|
||||
modalLabel: AppLocalizations.of(context)!.startMessageModalLabel,
|
||||
fontSize: 20,
|
||||
color: kPrimaryColor,
|
||||
initialValue: gameDTO.messageDebut ?? [],
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/category_list.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
|
||||
class CategoryInputContainer extends StatefulWidget {
|
||||
final Color color;
|
||||
@ -51,7 +52,7 @@ class _CategoryInputContainerState extends State<CategoryInputContainer> {
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
List<CategorieDTO> newValues = <CategorieDTO>[];
|
||||
showCreateOrUpdateCategories("Catégories", middleCategories, newValues, (value) {
|
||||
showCreateOrUpdateCategories(AppLocalizations.of(context)!.categoriesTitle, middleCategories, newValues, (value) {
|
||||
setState(() {
|
||||
widget.onChanged(value);
|
||||
middleCategories = value;
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/new_update_categorie.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -126,7 +127,7 @@ class _CategoryListState extends State<CategoryList> {
|
||||
onTap: () async {
|
||||
CategorieDTO newCategory = CategorieDTO(order: null);
|
||||
|
||||
var result = await showNewOrUpdateCategory(newCategory, appContext, context, "Création catégorie", currentIndex);
|
||||
var result = await showNewOrUpdateCategory(newCategory, appContext, context, AppLocalizations.of(context)!.createCategoryTitle, currentIndex);
|
||||
if (result != null)
|
||||
{
|
||||
setState(() {
|
||||
@ -203,7 +204,7 @@ class _CategoryListState extends State<CategoryList> {
|
||||
message: "Modifier",
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
var result = await showNewOrUpdateCategory(category, appContext, context, "Modification catégorie", currentIndex);
|
||||
var result = await showNewOrUpdateCategory(category, appContext, context, AppLocalizations.of(context)!.editCategoryTitle, currentIndex);
|
||||
if (result != null)
|
||||
{
|
||||
setState(() {
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/listVi
|
||||
import 'package:manager_app/Screens/Resources/select_resource_modal.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -89,7 +90,7 @@ class _GeoPointContentListState extends State<GeoPointContentList> {
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
var result = await showSelectResourceModal(
|
||||
"Sélectionner une ressource",
|
||||
AppLocalizations.of(context)!.selectResource,
|
||||
1,
|
||||
[ResourceType.Image, ResourceType.ImageUrl, ResourceType.Video, ResourceType.VideoUrl, ResourceType.Audio],
|
||||
context,
|
||||
|
||||
@ -20,6 +20,7 @@ import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/showNe
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/client.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Parcours/parcours_config.dart';
|
||||
|
||||
@ -137,8 +138,8 @@ class _MapConfigState extends State<MapConfig> {
|
||||
unselectedLabelColor: Colors.grey,
|
||||
indicatorColor: kPrimaryColor,
|
||||
tabs: [
|
||||
Tab(icon: Icon(Icons.map), text: "Points d'Intérêt"),
|
||||
Tab(icon: Icon(Icons.route), text: "Parcours"),
|
||||
Tab(icon: Icon(Icons.map), text: AppLocalizations.of(context)!.pointsOfInterestLabel),
|
||||
Tab(icon: Icon(Icons.route), text: AppLocalizations.of(context)!.pathsLabel),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
@ -293,7 +294,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
top: 10,
|
||||
left: 10,
|
||||
child: Text(
|
||||
"Points géographiques",
|
||||
AppLocalizations.of(context)!.geopointsLabel,
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
),
|
||||
@ -351,7 +352,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
constraints:
|
||||
BoxConstraints(minHeight: 85),
|
||||
child: StringInputContainer(
|
||||
label: "Recherche:",
|
||||
label: AppLocalizations.of(context)!.searchLabel,
|
||||
isSmall: true,
|
||||
fontSize: 15,
|
||||
fontSizeText: 15,
|
||||
@ -379,7 +380,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Le point géographique a été créé avec succès',
|
||||
AppLocalizations.of(context)!.geopointCreatedSuccess,
|
||||
context,
|
||||
null);
|
||||
setState(() {
|
||||
@ -390,7 +391,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Une erreur est survenue lors de la création du point géographique',
|
||||
AppLocalizations.of(context)!.geopointCreateError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -428,7 +429,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
} else {
|
||||
return Center(
|
||||
child: Text(
|
||||
"Une erreur est survenue lors de la récupération des points géographiques"),
|
||||
AppLocalizations.of(context)!.geopointsLoadError),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -507,7 +508,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Le point géographique a été mis à jour avec succès',
|
||||
AppLocalizations.of(context)!.geopointUpdatedSuccess,
|
||||
context,
|
||||
null);
|
||||
setState(() {
|
||||
@ -518,7 +519,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Une erreur est survenue lors de la mise à jour du point géographique',
|
||||
AppLocalizations.of(context)!.geopointUpdateError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -547,7 +548,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir supprimer ce point géographique ?",
|
||||
AppLocalizations.of(context)!.geopointDeleteConfirm,
|
||||
() {}, () async {
|
||||
try {
|
||||
var pointToRemove = pointsToShow[index];
|
||||
@ -558,7 +559,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Le point géographique a été supprimé avec succès',
|
||||
AppLocalizations.of(context)!.geopointDeletedSuccess,
|
||||
context,
|
||||
null);
|
||||
// refresh UI
|
||||
@ -569,7 +570,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Une erreur est survenue lors de la suppression du point géographique',
|
||||
AppLocalizations.of(context)!.geopointDeleteError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -609,7 +610,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
SingleSelectContainer(
|
||||
label: "Service :",
|
||||
label: AppLocalizations.of(context)!.serviceLabel,
|
||||
color: Colors.black,
|
||||
initialValue: mapProviderIn,
|
||||
inputValues: map_providers,
|
||||
@ -625,7 +626,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
widget.onChanged(mapDTO);
|
||||
}),
|
||||
GeolocInputContainer(
|
||||
label: "Point de centrage :",
|
||||
label: AppLocalizations.of(context)!.centerPointLabel,
|
||||
initialValue: mapDTO.centerLatitude != null &&
|
||||
mapDTO.centerLongitude != null
|
||||
? LatLong(double.parse(mapDTO.centerLatitude!),
|
||||
@ -642,7 +643,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
},
|
||||
isSmall: true),
|
||||
ResourceInputContainer(
|
||||
label: "Icône :",
|
||||
label: AppLocalizations.of(context)!.iconLabel,
|
||||
initialValue: mapDTO.iconResourceId,
|
||||
color: kPrimaryColor,
|
||||
imageFit: BoxFit.contain,
|
||||
@ -664,7 +665,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
CheckInputContainer(
|
||||
label: "Vue liste :",
|
||||
label: AppLocalizations.of(context)!.listViewLabel,
|
||||
isChecked: mapDTO.isListViewEnabled ?? false,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
@ -675,7 +676,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
),
|
||||
if (mapDTO.mapProvider == MapProvider.Google)
|
||||
DropDownInputContainer(
|
||||
label: "Type :",
|
||||
label: AppLocalizations.of(context)!.typeLabel,
|
||||
values: map_types,
|
||||
initialValue: mapType,
|
||||
onChange: (String? value) {
|
||||
@ -685,7 +686,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
),
|
||||
if (mapDTO.mapProvider == MapProvider.MapBox)
|
||||
DropDownInputContainer(
|
||||
label: "Type :",
|
||||
label: AppLocalizations.of(context)!.typeLabel,
|
||||
values: map_types_mapBox,
|
||||
initialValue: mapTypeMapBox,
|
||||
onChange: (String? value) {
|
||||
@ -694,7 +695,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
},
|
||||
),
|
||||
SliderInputContainer(
|
||||
label: "Zoom :",
|
||||
label: AppLocalizations.of(context)!.zoomLabel,
|
||||
initialValue:
|
||||
mapDTO.zoom != null ? mapDTO.zoom!.toDouble() : 18,
|
||||
color: kPrimaryColor,
|
||||
@ -708,7 +709,7 @@ class _MapConfigState extends State<MapConfig> {
|
||||
Container(
|
||||
height: 70,
|
||||
child: CategoryInputContainer(
|
||||
label: "Catégories :",
|
||||
label: AppLocalizations.of(context)!.categoriesLabel,
|
||||
initialValue:
|
||||
mapDTO.categories != null ? mapDTO.categories! : [],
|
||||
color: kPrimaryColor,
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
Future<CategorieDTO?> showNewOrUpdateCategory(CategorieDTO? inputCategorieDTO, AppContext appContext, BuildContext context, String text, int currentIndex) async {
|
||||
@ -60,7 +61,7 @@ Future<CategorieDTO?> showNewOrUpdateCategory(CategorieDTO? inputCategorieDTO, A
|
||||
height: size.height * 0.2,
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
child: MultiStringInputContainer(
|
||||
label: "Nom affiché :",
|
||||
label: AppLocalizations.of(context)!.displayedNameLabel,
|
||||
modalLabel: text,
|
||||
fontSize: 20,
|
||||
color: kPrimaryColor,
|
||||
@ -79,7 +80,7 @@ Future<CategorieDTO?> showNewOrUpdateCategory(CategorieDTO? inputCategorieDTO, A
|
||||
height: size.height * 0.2,
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
child: ResourceInputContainer(
|
||||
label: "Icône catégorie :",
|
||||
label: AppLocalizations.of(context)!.categoryIconLabel,
|
||||
initialValue: categorieDTO.resourceDTO?.id,
|
||||
color: kPrimaryColor,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
@ -124,7 +125,7 @@ Future<CategorieDTO?> showNewOrUpdateCategory(CategorieDTO? inputCategorieDTO, A
|
||||
width: inputCategorieDTO != null ? 220: 150,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: inputCategorieDTO != null ? "Sauvegarder" : "Créer",
|
||||
text: inputCategorieDTO != null ? AppLocalizations.of(context)!.save : AppLocalizations.of(context)!.create,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
|
||||
@ -9,6 +9,7 @@ import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/geopoint_image_list.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO,
|
||||
@ -67,7 +68,7 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO,
|
||||
children: [
|
||||
// Titre du dialog
|
||||
Text(
|
||||
"Point géographique / Zone",
|
||||
AppLocalizations.of(context)!.geopointZoneTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -82,7 +83,7 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO,
|
||||
children: [
|
||||
// Géométrie
|
||||
GeometryInputContainer(
|
||||
label: "Géométrie (Point/Ligne/Zone) :",
|
||||
label: AppLocalizations.of(context)!.geometryTypeLabel,
|
||||
initialGeometry: geoPointDTO.geometry,
|
||||
initialColor: geoPointDTO.polyColor,
|
||||
onSave: (geometry, color) {
|
||||
@ -176,7 +177,7 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO,
|
||||
width: thirdWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Tel :",
|
||||
modalLabel: "Téléphone",
|
||||
modalLabel: AppLocalizations.of(context)!.phoneModalLabel,
|
||||
fontSize: 16,
|
||||
isHTML: true,
|
||||
color: kPrimaryColor,
|
||||
@ -255,7 +256,7 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO,
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: DropDownInputContainerCategories(
|
||||
label: "Catégorie :",
|
||||
label: AppLocalizations.of(context)!.categoryLabel,
|
||||
categories: mapDTO.categories!,
|
||||
initialValue: geoPointDTO.categorieId,
|
||||
onChange: (CategorieDTO? value) {
|
||||
@ -326,7 +327,7 @@ void showNewOrUpdateGeoPoint(MapDTO mapDTO, GeoPointDTO? inputGeoPointDTO,
|
||||
height: 46,
|
||||
child: RoundedButton(
|
||||
text:
|
||||
geoPointDTO.id != null ? "Sauvegarder" : "Créer",
|
||||
geoPointDTO.id != null ? AppLocalizations.of(context)!.save : AppLocalizations.of(context)!.create,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/Components/confirmation_dialog.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/fetch_section_icon.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
@ -63,17 +64,18 @@ class _ListViewCardSubSection extends State<ListViewCardSubSection> {
|
||||
showEditSubSection(
|
||||
widget.listItems[widget.index],
|
||||
(Object value) async {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
try {
|
||||
var test = updateSectionDetail(widget.listItems[widget.index], value);
|
||||
// update sub section
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionApi!.sectionUpdate(test);
|
||||
showNotification(kSuccess, kWhite, "La sous section a été mis à jour avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, l.subSectionUpdatedSuccess, context, null);
|
||||
setState(() {
|
||||
//widget.listItems[widget.index] = value;
|
||||
widget.onChanged(widget.listItems); // For resfreh ui
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, "Une erreur est survenue lors de la mise à jour de la sous section", context, null);
|
||||
showNotification(kError, kWhite, l.subSectionUpdateError, context, null);
|
||||
}
|
||||
},
|
||||
widget.appContext,
|
||||
@ -92,19 +94,20 @@ class _ListViewCardSubSection extends State<ListViewCardSubSection> {
|
||||
),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir supprimer cette sous section ?",
|
||||
l.sectionDeleteConfirm,
|
||||
() {},
|
||||
() async {
|
||||
try {
|
||||
var sectionToDelete = widget.listItems[widget.index];
|
||||
ManagerAppContext managerAppContext = appContext.getContext();
|
||||
await managerAppContext.clientAPI!.sectionApi!.sectionDelete(sectionToDelete.id!);
|
||||
showNotification(kSuccess, kWhite, 'La sous section a été supprimée avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, l.subSectionDeletedSuccess, context, null);
|
||||
// refresh UI
|
||||
widget.onChanged(widget.listItems);
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors de la suppression de la sous section', context, null);
|
||||
showNotification(kError, kWhite, l.subSectionDeleteError, context, null);
|
||||
}
|
||||
},
|
||||
context
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:js_interop';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Menu/listView_card_subSection.dart';
|
||||
@ -63,13 +64,13 @@ class _MenuConfigState extends State<MenuConfig> {
|
||||
|
||||
item.order = newIndex;
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionApi!.sectionUpdate(item);
|
||||
showNotification(kSuccess, kWhite, "L'ordre des sous sections a été mis à jour avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.subSectionOrderUpdatedSuccess, context, null);
|
||||
setState(() {
|
||||
// refresh ui
|
||||
print("Refresh UI");
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, "Une erreur est survenue lors de la mise à jour de l'ordre des sous sections", context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.subSectionOrderUpdateError, context, null);
|
||||
}
|
||||
|
||||
/*setState(
|
||||
@ -143,7 +144,7 @@ class _MenuConfigState extends State<MenuConfig> {
|
||||
);
|
||||
} else {
|
||||
return Center(child: Text(
|
||||
"Une erreur est survenue lors de la récupération des sous sections"));
|
||||
AppLocalizations.of(context)!.errorOccurred));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,14 +169,14 @@ class _MenuConfigState extends State<MenuConfig> {
|
||||
newSubsection.isBeacon = false;
|
||||
newSubsection.isActive = true;
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionApi!.sectionCreate(newSubsection);
|
||||
showNotification(kSuccess, kWhite, 'La sous section a été créée avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.subSectionCreatedSuccess, context, null);
|
||||
setState(() {
|
||||
// refresh ui
|
||||
print("Refresh UI");
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors de la création de la sous section', context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.subSectionCreateError, context, null);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@ -15,6 +15,7 @@ import 'package:manager_app/Screens/Configurations/Section/SubSection/Weather/we
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/WebOrVideo/web_config.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
import 'menu_config.dart';
|
||||
@ -77,7 +78,7 @@ void showEditSubSection(SectionDTO subSectionDTO, Function getResult, AppContext
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
child: MultiStringInputContainer(
|
||||
label: "Titre affiché:",
|
||||
label: AppLocalizations.of(context)!.displayTitleLabel,
|
||||
modalLabel: "Titre",
|
||||
fontSize: 20,
|
||||
isHTML: true,
|
||||
@ -95,7 +96,7 @@ void showEditSubSection(SectionDTO subSectionDTO, Function getResult, AppContext
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
child: MultiStringInputContainer(
|
||||
label: "Description affichée:",
|
||||
label: AppLocalizations.of(context)!.displayedDescriptionLabel,
|
||||
modalLabel: "Description",
|
||||
fontSize: 20,
|
||||
isHTML: true,
|
||||
@ -129,7 +130,7 @@ void showEditSubSection(SectionDTO subSectionDTO, Function getResult, AppContext
|
||||
appContext,
|
||||
(updatedData) {
|
||||
updatedRawSubSectionData = updatedData;
|
||||
}),
|
||||
}, context),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -183,7 +184,7 @@ void showEditSubSection(SectionDTO subSectionDTO, Function getResult, AppContext
|
||||
);
|
||||
}
|
||||
|
||||
getSpecificData(SectionDTO subSectionDTO, Object? rawSectionData, Object sectionDetailDTO, AppContext appContext, Function(Object) onChanged) {
|
||||
getSpecificData(SectionDTO subSectionDTO, Object? rawSectionData, Object sectionDetailDTO, AppContext appContext, Function(Object) onChanged, [BuildContext? context]) {
|
||||
switch(subSectionDTO.type) {
|
||||
case SectionType.Map:
|
||||
MapDTO mapDTO = MapDTO.fromJson(rawSectionData)!;
|
||||
@ -207,7 +208,7 @@ getSpecificData(SectionDTO subSectionDTO, Object? rawSectionData, Object section
|
||||
VideoDTO videoDTO = VideoDTO.fromJson(rawSectionData)!;
|
||||
sectionDetailDTO = videoDTO;
|
||||
return VideoConfig(
|
||||
label: "Url de la vidéo:",
|
||||
label: context != null ? AppLocalizations.of(context)!.videoUrlLabel : "Url de la vidéo:",
|
||||
initialValue: videoDTO,
|
||||
onChanged: (VideoDTO updatedWebDTO) {
|
||||
onChanged(updatedWebDTO);
|
||||
|
||||
@ -8,6 +8,7 @@ import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
Future<OrderedTranslationAndResourceDTO?> showNewOrUpdatePDFFile(OrderedTranslationAndResourceDTO? inputPdfFile, AppContext appContext, BuildContext context, String text) async {
|
||||
@ -81,7 +82,7 @@ Future<OrderedTranslationAndResourceDTO?> showNewOrUpdatePDFFile(OrderedTranslat
|
||||
height: size.height * 0.2,
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
child: ResourceInputContainer(
|
||||
label: "Icône catégorie :",
|
||||
label: AppLocalizations.of(context)!.categoryIconLabel,
|
||||
initialValue: categorieDTO.iconResourceId,
|
||||
color: kPrimaryColor,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
@ -130,7 +131,7 @@ Future<OrderedTranslationAndResourceDTO?> showNewOrUpdatePDFFile(OrderedTranslat
|
||||
width: inputPdfFile != null ? 220: 150,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: inputPdfFile != null ? "Sauvegarder" : "Créer",
|
||||
text: inputPdfFile != null ? AppLocalizations.of(context)!.save : AppLocalizations.of(context)!.create,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/new_up
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/PDF/new_update_pdfFile.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -95,7 +96,7 @@ class _PDFListState extends State<PDFList> {
|
||||
onTap: () async {
|
||||
OrderedTranslationAndResourceDTO newPdfFile = OrderedTranslationAndResourceDTO(order: null);
|
||||
|
||||
var result = await showNewOrUpdatePDFFile(newPdfFile, appContext, context, "Création PDF");
|
||||
var result = await showNewOrUpdatePDFFile(newPdfFile, appContext, context, AppLocalizations.of(context)!.createPdfTitle);
|
||||
if (result != null)
|
||||
{
|
||||
setState(() {
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Parcours/showNewOrUpdateGuidedPath.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
|
||||
@ -68,11 +69,11 @@ class _ParcoursConfigState extends State<ParcoursConfig> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Parcours Guidés",
|
||||
Text(AppLocalizations.of(context)!.guidedPathsLabel,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
ElevatedButton.icon(
|
||||
icon: Icon(Icons.add),
|
||||
label: Text("Ajouter un parcours"),
|
||||
label: Text(AppLocalizations.of(context)!.addPath),
|
||||
onPressed: () {
|
||||
final appContext =
|
||||
Provider.of<AppContext>(context, listen: false);
|
||||
@ -111,13 +112,13 @@ class _ParcoursConfigState extends State<ParcoursConfig> {
|
||||
});
|
||||
}
|
||||
showNotification(kSuccess, kWhite,
|
||||
'Parcours créé avec succès', context, null);
|
||||
AppLocalizations.of(context)!.pathCreatedSuccess, context, null);
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la création du parcours',
|
||||
AppLocalizations.of(context)!.pathCreateError,
|
||||
context,
|
||||
null);
|
||||
rethrow; // Important so showNewOrUpdateGuidedPath knows it failed
|
||||
@ -134,7 +135,7 @@ class _ParcoursConfigState extends State<ParcoursConfig> {
|
||||
Expanded(
|
||||
child: paths.isEmpty
|
||||
? Center(
|
||||
child: Text("Aucun parcours configuré",
|
||||
child: Text(AppLocalizations.of(context)!.noPathConfigured,
|
||||
style: TextStyle(fontStyle: FontStyle.italic)))
|
||||
: ReorderableListView.builder(
|
||||
buildDefaultDragHandles: false,
|
||||
@ -164,7 +165,7 @@ class _ParcoursConfigState extends State<ParcoursConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la mise à jour de l\'ordre',
|
||||
AppLocalizations.of(context)!.pathOrderUpdateError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -185,10 +186,10 @@ class _ParcoursConfigState extends State<ParcoursConfig> {
|
||||
.firstWhere((t) => t.language == 'FR',
|
||||
orElse: () => path.title![0])
|
||||
.value ??
|
||||
"Parcours sans titre",
|
||||
AppLocalizations.of(context)!.pathNoTitle,
|
||||
)
|
||||
: Text("Parcours sans titre"),
|
||||
subtitle: Text("${path.steps?.length ?? 0} étapes"),
|
||||
: Text(AppLocalizations.of(context)!.pathNoTitle),
|
||||
subtitle: Text(AppLocalizations.of(context)!.stepsCount(path.steps?.length ?? 0)),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@ -223,7 +224,7 @@ class _ParcoursConfigState extends State<ParcoursConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Parcours mis à jour avec succès',
|
||||
AppLocalizations.of(context)!.pathUpdatedSuccess,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -231,7 +232,7 @@ class _ParcoursConfigState extends State<ParcoursConfig> {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la mise à jour du parcours',
|
||||
AppLocalizations.of(context)!.pathUpdateError,
|
||||
context,
|
||||
null);
|
||||
rethrow;
|
||||
@ -265,14 +266,14 @@ class _ParcoursConfigState extends State<ParcoursConfig> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Parcours supprimé avec succès',
|
||||
AppLocalizations.of(context)!.pathDeletedSuccess,
|
||||
context,
|
||||
null);
|
||||
} catch (e) {
|
||||
showNotification(
|
||||
kError,
|
||||
kWhite,
|
||||
'Erreur lors de la suppression du parcours',
|
||||
AppLocalizations.of(context)!.pathDeleteError,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Components/multi_string_input_container.dart';
|
||||
import 'package:manager_app/Components/check_input_container.dart';
|
||||
@ -107,7 +108,7 @@ void showNewOrUpdateGuidedPath(
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: CheckInputContainer(
|
||||
label: "Linéaire :",
|
||||
label: AppLocalizations.of(context)!.linearLabel,
|
||||
isChecked: workingPath.isLinear ?? false,
|
||||
onChanged: (val) => setState(
|
||||
() => workingPath.isLinear = val),
|
||||
@ -117,7 +118,7 @@ void showNewOrUpdateGuidedPath(
|
||||
SizedBox(
|
||||
width: thirdWidth,
|
||||
child: CheckInputContainer(
|
||||
label: "Réussite requise :",
|
||||
label: AppLocalizations.of(context)!.requiredSuccessLabel,
|
||||
isChecked:
|
||||
workingPath.requireSuccessToAdvance ??
|
||||
false,
|
||||
@ -144,7 +145,7 @@ void showNewOrUpdateGuidedPath(
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Étapes du parcours",
|
||||
Text(AppLocalizations.of(context)!.pathStepsLabel,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 15)),
|
||||
@ -174,7 +175,7 @@ void showNewOrUpdateGuidedPath(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(
|
||||
"Aucun point/étape configuré.",
|
||||
AppLocalizations.of(context)!.noStepConfigured,
|
||||
style: TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Colors.grey[600]),
|
||||
@ -198,8 +199,8 @@ void showNewOrUpdateGuidedPath(
|
||||
orElse: () =>
|
||||
step.title![0])
|
||||
.value ??
|
||||
"Étape $index"
|
||||
: "Étape $index",
|
||||
"${AppLocalizations.of(context)!.stepFallback} $index"
|
||||
: "${AppLocalizations.of(context)!.stepFallback} $index",
|
||||
),
|
||||
subtitle: isEscapeMode
|
||||
? Text(
|
||||
|
||||
@ -8,6 +8,7 @@ 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/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'showNewOrUpdateQuizQuestion.dart';
|
||||
|
||||
void showNewOrUpdateGuidedStep(
|
||||
@ -53,7 +54,7 @@ void showNewOrUpdateGuidedStep(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
step == null ? "Nouvelle Étape" : "Modifier l'Étape",
|
||||
step == null ? AppLocalizations.of(context)!.newStepTitle : AppLocalizations.of(context)!.editStepTitle,
|
||||
style: TextStyle(
|
||||
color: kPrimaryColor,
|
||||
fontSize: 20,
|
||||
@ -72,7 +73,7 @@ void showNewOrUpdateGuidedStep(
|
||||
width: halfWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Titre :",
|
||||
modalLabel: "Titre de l'étape",
|
||||
modalLabel: AppLocalizations.of(context)!.stepTitleLabel,
|
||||
initialValue: workingStep.title ?? [],
|
||||
onGetResult: (val) =>
|
||||
setState(() => workingStep.title = val),
|
||||
@ -86,7 +87,7 @@ void showNewOrUpdateGuidedStep(
|
||||
width: halfWidth,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Description :",
|
||||
modalLabel: "Description de l'étape",
|
||||
modalLabel: AppLocalizations.of(context)!.stepDescriptionLabel,
|
||||
initialValue: workingStep.description ?? [],
|
||||
onGetResult: (val) => setState(
|
||||
() => workingStep.description = val),
|
||||
@ -100,7 +101,7 @@ void showNewOrUpdateGuidedStep(
|
||||
Divider(height: 24),
|
||||
// Géométrie — Directement avec GeometryDTO
|
||||
GeometryInputContainer(
|
||||
label: "Emplacement de l'étape :",
|
||||
label: AppLocalizations.of(context)!.stepLocationLabel,
|
||||
initialGeometry: workingStep.geometry,
|
||||
initialColor: null,
|
||||
onSave: (geometry, color) {
|
||||
@ -118,7 +119,7 @@ void showNewOrUpdateGuidedStep(
|
||||
Expanded(
|
||||
child: SwitchListTile(
|
||||
dense: true,
|
||||
title: Text("Cachée initialement"),
|
||||
title: Text(AppLocalizations.of(context)!.initiallyHiddenLabel),
|
||||
value: workingStep.isHiddenInitially ?? false,
|
||||
onChanged: (val) => setState(() => workingStep.isHiddenInitially = val),
|
||||
activeThumbColor: kPrimaryColor,
|
||||
@ -127,7 +128,7 @@ void showNewOrUpdateGuidedStep(
|
||||
Expanded(
|
||||
child: SwitchListTile(
|
||||
dense: true,
|
||||
title: Text("Verrouillée"),
|
||||
title: Text(AppLocalizations.of(context)!.lockedLabel),
|
||||
value: workingStep.isStepLocked ?? false,
|
||||
onChanged: (val) => setState(() => workingStep.isStepLocked = val),
|
||||
activeThumbColor: kPrimaryColor,
|
||||
@ -151,7 +152,7 @@ void showNewOrUpdateGuidedStep(
|
||||
SizedBox(
|
||||
width: halfWidth,
|
||||
child: StringInputContainer(
|
||||
label: "Durée (secondes) :",
|
||||
label: AppLocalizations.of(context)!.durationSecondsLabel,
|
||||
initialValue: workingStep.timerSeconds?.toString() ?? "",
|
||||
onChanged: (val) => setState(() =>
|
||||
workingStep.timerSeconds = int.tryParse(val)),
|
||||
@ -176,7 +177,7 @@ void showNewOrUpdateGuidedStep(
|
||||
],
|
||||
SizedBox(height: 8),
|
||||
StringInputContainer(
|
||||
label: "GeoPoint de déclenchement (ID) :",
|
||||
label: AppLocalizations.of(context)!.triggerGeopointLabel,
|
||||
initialValue: workingStep.triggerGeoPointId?.toString() ?? "",
|
||||
onChanged: (val) => setState(() =>
|
||||
workingStep.triggerGeoPointId = int.tryParse(val)),
|
||||
@ -187,7 +188,7 @@ void showNewOrUpdateGuidedStep(
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Questions / Défis",
|
||||
Text(AppLocalizations.of(context)!.questionsChallengesLabel,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 15)),
|
||||
@ -220,7 +221,7 @@ void showNewOrUpdateGuidedStep(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(
|
||||
"Aucune question configurée.",
|
||||
AppLocalizations.of(context)!.noQuestionsConfigured,
|
||||
style: TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Colors.grey[600]),
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Components/multi_string_input_container.dart';
|
||||
import 'package:manager_app/Components/resource_input_container.dart';
|
||||
@ -104,8 +105,8 @@ void showNewOrUpdateQuizQuestion(
|
||||
children: [
|
||||
// --- Intitulé (multi-langue via MultiStringInputContainer) ---
|
||||
MultiStringInputContainer(
|
||||
label: "Question posée :",
|
||||
modalLabel: "Intitulé de la question",
|
||||
label: AppLocalizations.of(context)!.questionAskedLabel,
|
||||
modalLabel: AppLocalizations.of(context)!.questionTitleLabel,
|
||||
initialValue: workingLabel,
|
||||
onGetResult: (val) => setState(() {
|
||||
workingLabel = val;
|
||||
@ -146,7 +147,7 @@ void showNewOrUpdateQuizQuestion(
|
||||
if (workingQuestion.validationQuestionType ==
|
||||
QuestionType.number0) ...[
|
||||
Divider(height: 24),
|
||||
Text("Réponse attendue :",
|
||||
Text(AppLocalizations.of(context)!.expectedAnswerLabel,
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
SizedBox(height: 8),
|
||||
Builder(builder: (_) {
|
||||
@ -156,7 +157,7 @@ void showNewOrUpdateQuizQuestion(
|
||||
workingQuestion.responses[0].label);
|
||||
return MultiStringInputContainer(
|
||||
label: "",
|
||||
modalLabel: "Réponse attendue",
|
||||
modalLabel: AppLocalizations.of(context)!.expectedAnswerModalLabel,
|
||||
initialValue: respLabel,
|
||||
onGetResult: (val) => setState(() {
|
||||
workingQuestion.responses[0].label =
|
||||
@ -170,7 +171,7 @@ void showNewOrUpdateQuizQuestion(
|
||||
}),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
"La validation se fait par comparaison (insensible à la casse).",
|
||||
AppLocalizations.of(context)!.validationNote,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontStyle: FontStyle.italic,
|
||||
@ -187,7 +188,7 @@ void showNewOrUpdateQuizQuestion(
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Réponses possibles :",
|
||||
Text(AppLocalizations.of(context)!.possibleAnswersLabel,
|
||||
style:
|
||||
TextStyle(fontWeight: FontWeight.bold)),
|
||||
TextButton.icon(
|
||||
@ -208,7 +209,7 @@ void showNewOrUpdateQuizQuestion(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(
|
||||
"Aucune réponse définie. Ajoutez-en au moins une.",
|
||||
AppLocalizations.of(context)!.noAnswerDefined,
|
||||
style: TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Colors.grey[600]),
|
||||
@ -233,8 +234,8 @@ void showNewOrUpdateQuizQuestion(
|
||||
// Checkbox bonne réponse
|
||||
Tooltip(
|
||||
message: resp.isGood == true
|
||||
? "Bonne réponse ✓"
|
||||
: "Mauvaise réponse",
|
||||
? AppLocalizations.of(context)!.correctAnswer
|
||||
: AppLocalizations.of(context)!.wrongAnswer,
|
||||
child: Checkbox(
|
||||
value: resp.isGood ?? false,
|
||||
activeColor: kSuccess,
|
||||
@ -245,8 +246,8 @@ void showNewOrUpdateQuizQuestion(
|
||||
// Traductions (via MultiStringInputContainer)
|
||||
Expanded(
|
||||
child: MultiStringInputContainer(
|
||||
label: "Réponse ${i + 1} :",
|
||||
modalLabel: "Réponse ${i + 1}",
|
||||
label: "${AppLocalizations.of(context)!.answerLabel} ${i + 1} :",
|
||||
modalLabel: "${AppLocalizations.of(context)!.answerLabel} ${i + 1}",
|
||||
initialValue: respLabel,
|
||||
onGetResult: (val) => setState(
|
||||
() => resp.label =
|
||||
@ -273,7 +274,7 @@ void showNewOrUpdateQuizQuestion(
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
"✓ = bonne réponse. Plusieurs peuvent être correctes.",
|
||||
AppLocalizations.of(context)!.answerNote,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontStyle: FontStyle.italic,
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Screens/Configurations/Section/SubSection/Quizz/quizz_answer_list.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO, AppContext appContext, BuildContext context, String text) async {
|
||||
@ -54,7 +55,7 @@ Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
ResourceInputContainer(
|
||||
label: "Image fond d'écran :",
|
||||
label: AppLocalizations.of(context)!.backgroundImageLabel,
|
||||
initialValue: questionDTO.imageBackgroundResourceId,
|
||||
color: kPrimaryColor,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
@ -75,8 +76,8 @@ Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO,
|
||||
height: size.height * 0.15,
|
||||
constraints: BoxConstraints(minHeight: 50, maxHeight: 80),
|
||||
child: MultiStringInputAndResourceContainer(
|
||||
label: "Question :",
|
||||
modalLabel: "Question",
|
||||
label: AppLocalizations.of(context)!.questionInputLabel,
|
||||
modalLabel: AppLocalizations.of(context)!.questionLabel,
|
||||
fontSize: 20,
|
||||
color: kPrimaryColor,
|
||||
resourceTypes: [ResourceType.Image, ResourceType.ImageUrl, ResourceType.Video, ResourceType.VideoUrl, ResourceType.Audio],
|
||||
@ -133,7 +134,7 @@ Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO,
|
||||
width: 175,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Annuler",
|
||||
text: AppLocalizations.of(context)!.cancel,
|
||||
icon: Icons.undo,
|
||||
color: kSecond,
|
||||
press: () {
|
||||
@ -149,7 +150,7 @@ Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO,
|
||||
width: inputQuestionDTO != null ? 220: 150,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: inputQuestionDTO != null ? "Sauvegarder" : "Créer",
|
||||
text: inputQuestionDTO != null ? AppLocalizations.of(context)!.save : AppLocalizations.of(context)!.create,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
@ -157,7 +158,7 @@ Future<QuestionDTO?> showNewOrUpdateQuestionQuizz(QuestionDTO? inputQuestionDTO,
|
||||
if(!questionDTO.label!.any((label) => label.value == null || label.value!.trim() == "")) {
|
||||
Navigator.pop(dialogContext, questionDTO);
|
||||
} else {
|
||||
showNotification(kPrimaryColor, kWhite, "La traduction n'est pas complète", context, null);
|
||||
showNotification(kPrimaryColor, kWhite, AppLocalizations.of(context)!.translationIncomplete, context, null);
|
||||
}
|
||||
},
|
||||
fontSize: 20,
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:manager_app/Components/multi_string_input_html_modal.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -92,7 +93,7 @@ class _QuizzResponseListState extends State<QuizzResponseList> {
|
||||
top: 10,
|
||||
left: 10,
|
||||
child: Text(
|
||||
"Réponses",
|
||||
AppLocalizations.of(context)!.answersLabel,
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
@ -117,7 +118,7 @@ class _QuizzResponseListState extends State<QuizzResponseList> {
|
||||
}
|
||||
});
|
||||
|
||||
showMultiStringInputAndResourceHTML("Réponse", "Créer la réponse", true, initials, newValues, (value) {
|
||||
showMultiStringInputAndResourceHTML(AppLocalizations.of(context)!.answerLabel, AppLocalizations.of(context)!.createAnswerTitle, true, initials, newValues, (value) {
|
||||
if(value != null && value.isNotEmpty) {
|
||||
setState(() {
|
||||
newResponse.label = value;
|
||||
@ -184,7 +185,7 @@ class _QuizzResponseListState extends State<QuizzResponseList> {
|
||||
child: Row(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: "Si coché, la réponse est valide",
|
||||
message: AppLocalizations.of(context)!.answerValidNote,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Checkbox(
|
||||
@ -217,7 +218,7 @@ class _QuizzResponseListState extends State<QuizzResponseList> {
|
||||
}
|
||||
});
|
||||
|
||||
showMultiStringInputAndResourceHTML("Réponse", "Modifier la réponse", true, initials, newValues, (value) {
|
||||
showMultiStringInputAndResourceHTML(AppLocalizations.of(context)!.answerLabel, AppLocalizations.of(context)!.editAnswerTitle, true, initials, newValues, (value) {
|
||||
setState(() {
|
||||
if (value != null && response.label! != value) {
|
||||
response.label = value;
|
||||
|
||||
@ -8,6 +8,7 @@ import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/client.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'dart:convert';
|
||||
@ -62,13 +63,13 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
item.order = newIndex;
|
||||
try {
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionQuizApi!.sectionQuizUpdate(item);
|
||||
showNotification(kSuccess, kWhite, "L'ordre des questions a été mis à jour avec succès", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.questionOrderUpdatedSuccess, context, null);
|
||||
setState(() {
|
||||
// refresh ui
|
||||
print("Refresh UI");
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, "Une erreur est survenue lors de la mise à jour de l'ordre des questions", context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.questionOrderUpdateError, context, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,12 +87,12 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
Container(
|
||||
height: 50,
|
||||
child: RoundedButton(
|
||||
text: "Mauvais score",
|
||||
text: AppLocalizations.of(context)!.quizBadScore,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
icon: Icons.message,
|
||||
press: () async {
|
||||
updateScoreQuizMessage(context, appContext, quizzDTO.badLevel, "Message pour un mauvais score", 0);
|
||||
updateScoreQuizMessage(context, appContext, quizzDTO.badLevel, AppLocalizations.of(context)!.quizBadScoreMsg, 0);
|
||||
},
|
||||
fontSize: 20,
|
||||
horizontal: 10,
|
||||
@ -102,12 +103,12 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
Container(
|
||||
height: 50,
|
||||
child: RoundedButton(
|
||||
text: "Moyen score",
|
||||
text: AppLocalizations.of(context)!.quizMediumScore,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
icon: Icons.message,
|
||||
press: () async {
|
||||
updateScoreQuizMessage(context, appContext, quizzDTO.mediumLevel, "Message pour un score moyen", 1);
|
||||
updateScoreQuizMessage(context, appContext, quizzDTO.mediumLevel, AppLocalizations.of(context)!.quizMediumScoreMsg, 1);
|
||||
},
|
||||
fontSize: 20,
|
||||
horizontal: 10,
|
||||
@ -118,12 +119,12 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
Container(
|
||||
height: 50,
|
||||
child: RoundedButton(
|
||||
text: "Bon score",
|
||||
text: AppLocalizations.of(context)!.quizGoodScore,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
icon: Icons.message,
|
||||
press: () async {
|
||||
updateScoreQuizMessage(context, appContext, quizzDTO.goodLevel, "Message pour un bon score", 2);
|
||||
updateScoreQuizMessage(context, appContext, quizzDTO.goodLevel, AppLocalizations.of(context)!.quizGoodScoreMsg, 2);
|
||||
},
|
||||
fontSize: 20,
|
||||
horizontal: 10,
|
||||
@ -134,12 +135,12 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
Container(
|
||||
height: 50,
|
||||
child: RoundedButton(
|
||||
text: "Excellent score",
|
||||
text: AppLocalizations.of(context)!.quizExcellentScore,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
icon: Icons.message,
|
||||
press: () async {
|
||||
updateScoreQuizMessage(context, appContext, quizzDTO.greatLevel, "Message pour un excellent score", 3);
|
||||
updateScoreQuizMessage(context, appContext, quizzDTO.greatLevel, AppLocalizations.of(context)!.quizExcellentScoreMsg, 3);
|
||||
},
|
||||
fontSize: 20,
|
||||
horizontal: 10,
|
||||
@ -204,7 +205,7 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
top: 10,
|
||||
left: 10,
|
||||
child: Text(
|
||||
"Questions",
|
||||
AppLocalizations.of(context)!.questionsLabel,
|
||||
style: TextStyle(fontSize: 15,
|
||||
fontWeight: FontWeight.w500),
|
||||
),
|
||||
@ -215,20 +216,20 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
QuestionDTO? result = await showNewOrUpdateQuestionQuizz(
|
||||
null, appContext, context, "Question");
|
||||
null, appContext, context, AppLocalizations.of(context)!.questionLabel);
|
||||
|
||||
try {
|
||||
if(result != null)
|
||||
{
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionQuizApi!.sectionQuizCreate(quizzDTO.id!, result);
|
||||
showNotification(kSuccess, kWhite, 'La question a été créée avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.questionCreatedSuccess, context, null);
|
||||
setState(() {
|
||||
// refresh ui
|
||||
print("Refresh UI");
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors de la création de la question', context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.questionCreateError, context, null);
|
||||
}
|
||||
|
||||
/*setState(() {
|
||||
@ -267,7 +268,7 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
);
|
||||
} else {
|
||||
return Center(child: Text(
|
||||
"Une erreur est survenue lors de la récupération des questions"));
|
||||
AppLocalizations.of(context)!.questionsLoadError));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -314,28 +315,28 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
child: Row(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: "Modifier",
|
||||
message: AppLocalizations.of(context)!.edit,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
QuestionDTO? result = await showNewOrUpdateQuestionQuizz(
|
||||
question,
|
||||
appContext,
|
||||
context,
|
||||
"Modifier la question"
|
||||
AppLocalizations.of(context)!.editQuestion
|
||||
);
|
||||
|
||||
try {
|
||||
if(result != null)
|
||||
{
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.sectionQuizApi!.sectionQuizUpdate(result);
|
||||
showNotification(kSuccess, kWhite, 'La question a été mis à jour avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.questionUpdatedSuccess, context, null);
|
||||
setState(() {
|
||||
// refresh ui
|
||||
print("Refresh UI");
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors de la mise à jour de la question', context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.questionUpdateError, context, null);
|
||||
}
|
||||
|
||||
/*setState(() {
|
||||
@ -354,24 +355,24 @@ class _QuizzConfigState extends State<QuizzConfig> {
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Supprimer",
|
||||
message: AppLocalizations.of(context)!.delete,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir supprimer cette question ?",
|
||||
AppLocalizations.of(context)!.questionDeleteConfirm,
|
||||
() {},
|
||||
() async {
|
||||
|
||||
try {
|
||||
var questionToRemove = questions[index];
|
||||
(appContext.getContext() as ManagerAppContext).clientAPI!.sectionQuizApi!.sectionQuizDeleteWithHttpInfo(questionToRemove.id!);
|
||||
showNotification(kSuccess, kWhite, 'La question a été supprimée avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.questionDeletedSuccess, context, null);
|
||||
// refresh UI
|
||||
setState(() {
|
||||
print("Refresh UI");
|
||||
});
|
||||
} catch(e) {
|
||||
showNotification(kError, kWhite, 'Une erreur est survenue lors de la suppression de la question', context, null);
|
||||
showNotification(kError, kWhite, AppLocalizations.of(context)!.questionDeleteError, context, null);
|
||||
}
|
||||
},
|
||||
context
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
Future<ContentDTO?> showNewOrUpdateContentSlider(ContentDTO? inputContentDTO, AppContext appContext, BuildContext context, bool showTitle, bool showDescription) async {
|
||||
@ -75,7 +76,7 @@ Future<ContentDTO?> showNewOrUpdateContentSlider(ContentDTO? inputContentDTO, Ap
|
||||
child: Column(
|
||||
children: [
|
||||
MultiStringInputContainer(
|
||||
label: "Titre affiché:",
|
||||
label: AppLocalizations.of(context)!.displayTitleLabel,
|
||||
modalLabel: "Titre",
|
||||
fontSize: 20,
|
||||
isHTML: true,
|
||||
@ -90,7 +91,7 @@ Future<ContentDTO?> showNewOrUpdateContentSlider(ContentDTO? inputContentDTO, Ap
|
||||
isTitle: true
|
||||
),
|
||||
MultiStringInputContainer(
|
||||
label: "Description affichée:",
|
||||
label: AppLocalizations.of(context)!.displayedDescriptionLabel,
|
||||
modalLabel: "Description",
|
||||
fontSize: 20,
|
||||
isHTML: true,
|
||||
@ -140,7 +141,7 @@ Future<ContentDTO?> showNewOrUpdateContentSlider(ContentDTO? inputContentDTO, Ap
|
||||
width: inputContentDTO != null ? 220: 150,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: inputContentDTO != null ? "Sauvegarder" : "Créer",
|
||||
text: inputContentDTO != null ? AppLocalizations.of(context)!.save : AppLocalizations.of(context)!.create,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
|
||||
@ -10,6 +10,7 @@ import 'package:manager_app/Components/fetch_section_icon.dart';
|
||||
import 'package:manager_app/Components/resource_input_container.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.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/rounded_button.dart';
|
||||
@ -120,10 +121,10 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
} else {
|
||||
return Center(
|
||||
child: Text(
|
||||
"Une erreur est survenue lors de la récupération de la section"));
|
||||
AppLocalizations.of(context)!.sectionLoadError));
|
||||
}
|
||||
} else if (snapshot.connectionState == ConnectionState.none) {
|
||||
return Text("No data");
|
||||
return Text(AppLocalizations.of(context)!.noData);
|
||||
} else {
|
||||
return Center(
|
||||
child: Container(
|
||||
@ -235,7 +236,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Ce QR code a été copié dans le presse papier',
|
||||
AppLocalizations.of(context)!.qrCodeCopied,
|
||||
context,
|
||||
null);
|
||||
},
|
||||
@ -263,7 +264,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
],
|
||||
),
|
||||
CheckInputContainer(
|
||||
label: "Beacon :",
|
||||
label: AppLocalizations.of(context)!.beaconLabel,
|
||||
isChecked: sectionDTO.isBeacon!,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
@ -274,7 +275,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
),
|
||||
if (sectionDTO.isBeacon!)
|
||||
NumberInputContainer(
|
||||
label: "Identifiant Beacon :",
|
||||
label: AppLocalizations.of(context)!.beaconIdLabel,
|
||||
initialValue: sectionDTO.beaconId != null
|
||||
? sectionDTO.beaconId!
|
||||
: 0,
|
||||
@ -287,7 +288,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
showNotification(
|
||||
Colors.orange,
|
||||
kWhite,
|
||||
'Cela doit être un chiffre',
|
||||
AppLocalizations.of(context)!.beaconMustBeNumber,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -302,7 +303,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
SizedBox(
|
||||
height: 100,
|
||||
child: StringInputContainer(
|
||||
label: "Identifiant :",
|
||||
label: AppLocalizations.of(context)!.identifierLabel,
|
||||
initialValue: sectionDTO.label,
|
||||
onChanged: (String value) {
|
||||
sectionDTO.label = value;
|
||||
@ -312,8 +313,8 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
SizedBox(
|
||||
height: 100,
|
||||
child: MultiStringInputContainer(
|
||||
label: "Titre affiché:",
|
||||
modalLabel: "Titre",
|
||||
label: AppLocalizations.of(context)!.displayTitleLabel,
|
||||
modalLabel: AppLocalizations.of(context)!.messageTitle,
|
||||
color: kPrimaryColor,
|
||||
initialValue: sectionDTO.title!,
|
||||
onGetResult: (value) {
|
||||
@ -351,7 +352,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ResourceInputContainer(
|
||||
label: "Image :",
|
||||
label: AppLocalizations.of(context)!.imageLabel,
|
||||
initialValue: sectionDTO.imageId,
|
||||
color: kPrimaryColor,
|
||||
onChanged: (ResourceDTO resource) {
|
||||
@ -400,7 +401,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: RoundedButton(
|
||||
text: "Annuler",
|
||||
text: AppLocalizations.of(context)!.cancel,
|
||||
icon: Icons.undo,
|
||||
color: Colors.grey,
|
||||
textColor: Colors.white,
|
||||
@ -413,7 +414,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
if (canEdit) Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RoundedButton(
|
||||
text: "Supprimer",
|
||||
text: AppLocalizations.of(context)!.delete,
|
||||
icon: Icons.delete,
|
||||
color: kError,
|
||||
textColor: Colors.white,
|
||||
@ -426,7 +427,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
if (canEdit) Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RoundedButton(
|
||||
text: "Sauvegarder",
|
||||
text: AppLocalizations.of(context)!.save,
|
||||
icon: Icons.done,
|
||||
color: kSuccess,
|
||||
textColor: Colors.white,
|
||||
@ -456,7 +457,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
|
||||
Future<void> delete(AppContext appContext) async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir supprimer cette section ?", () {}, () async {
|
||||
AppLocalizations.of(context)!.sectionDeleteConfirm, () {}, () async {
|
||||
ManagerAppContext managerAppContext = appContext.getContext();
|
||||
await managerAppContext.clientAPI!.sectionApi!
|
||||
.sectionDelete(sectionDTO.id!);
|
||||
@ -482,12 +483,12 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
showNotification(
|
||||
kSuccess,
|
||||
kWhite,
|
||||
'Les traductions de la section ont été sauvegardées avec succès',
|
||||
AppLocalizations.of(context)!.sectionTranslationSaved,
|
||||
context,
|
||||
null);
|
||||
} else {
|
||||
showNotification(kSuccess, kWhite,
|
||||
'La section a été sauvegardée avec succès', context, null);
|
||||
AppLocalizations.of(context)!.sectionSavedSuccess, context, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -509,7 +510,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
);
|
||||
case SectionType.Video:
|
||||
return VideoConfig(
|
||||
label: "Url de la vidéo:",
|
||||
label: AppLocalizations.of(context)!.videoUrlLabel,
|
||||
initialValue: sectionDetailDTO as VideoDTO,
|
||||
onChanged: (VideoDTO updatedWebDTO) {
|
||||
sectionDetailDTO = updatedWebDTO;
|
||||
@ -517,7 +518,7 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
|
||||
);
|
||||
case SectionType.Web:
|
||||
return WebConfig(
|
||||
label: "Url du site web:",
|
||||
label: AppLocalizations.of(context)!.webUrlLabel,
|
||||
initialValue: sectionDetailDTO as WebDTO,
|
||||
onChanged: (WebDTO updatedWebDTO) {
|
||||
sectionDetailDTO = updatedWebDTO;
|
||||
|
||||
@ -11,6 +11,7 @@ import 'package:manager_app/Components/number_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/message_notification.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_string_input_container.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
@ -50,7 +51,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
return bodyConfiguration(snapshot.data, size, appContext, context);
|
||||
} else if (snapshot.connectionState == ConnectionState.none) {
|
||||
return Text("No data");
|
||||
return Text(AppLocalizations.of(context)!.noData);
|
||||
} else {
|
||||
return Center(
|
||||
child: Container(
|
||||
@ -100,11 +101,11 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
anchorElement.click();
|
||||
} else {
|
||||
File test = await FileHelper().storeConfiguration(export);
|
||||
showNotification(kSuccess, kWhite, "L'export de la configuration a réussi, le document se trouve à cet endroit : " + test.path, context, 3000);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configExportSuccess(test.path), context, 3000);
|
||||
}
|
||||
} catch(e) {
|
||||
log(e.toString());
|
||||
showNotification(kPrimaryColor, kWhite, "L'export de la configuration a échoué", context, null);
|
||||
showNotification(kPrimaryColor, kWhite, AppLocalizations.of(context)!.configExportFailed, context, null);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
@ -167,7 +168,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
SizedBox(
|
||||
height: 70,
|
||||
child: StringInputContainer(
|
||||
label: "Identifiant :",
|
||||
label: AppLocalizations.of(context)!.identifierLabel,
|
||||
fontSize: 20,
|
||||
fontSizeText: 20,
|
||||
initialValue: configurationDTO.label,
|
||||
@ -182,7 +183,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
MultiSelectDropdownLanguageContainer(
|
||||
label: "Langues :",
|
||||
label: AppLocalizations.of(context)!.languagesLabel,
|
||||
initialValue: configurationDTO.languages != null ? configurationDTO.languages! : [],
|
||||
values: languages,
|
||||
isMultiple: true,
|
||||
@ -195,7 +196,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
),
|
||||
CheckInputContainer(
|
||||
icon: Icons.signal_wifi_off,
|
||||
label: "Hors ligne :",
|
||||
label: "Hors ligne :", // no ARB key for this one yet, leave as-is
|
||||
fontSize: 20,
|
||||
isChecked: configurationDTO.isOffline,
|
||||
onChanged: (value) {
|
||||
@ -211,7 +212,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ResourceInputContainer(
|
||||
label: "Image fond d'écran :",
|
||||
label: AppLocalizations.of(context)!.mainImageLabel,
|
||||
fontSize: 20,
|
||||
initialValue: configurationDTO.imageId,
|
||||
color: kPrimaryColor,
|
||||
@ -226,7 +227,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
},
|
||||
),
|
||||
ResourceInputContainer(
|
||||
label: "Image loader :",
|
||||
label: AppLocalizations.of(context)!.loaderLabel,
|
||||
fontSize: 20,
|
||||
initialValue: configurationDTO.loaderImageId,
|
||||
color: kPrimaryColor,
|
||||
@ -355,7 +356,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: RoundedButton(
|
||||
text: "Annuler",
|
||||
text: AppLocalizations.of(context)!.cancel,
|
||||
icon: Icons.undo,
|
||||
color: Colors.grey,
|
||||
textColor: Colors.white,
|
||||
@ -368,7 +369,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
if (canEdit) Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: RoundedButton(
|
||||
text: "Supprimer",
|
||||
text: AppLocalizations.of(context)!.delete,
|
||||
icon: Icons.delete,
|
||||
color: kError,
|
||||
textColor: Colors.white,
|
||||
@ -381,7 +382,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
if (canEdit) Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: RoundedButton(
|
||||
text: "Sauvegarder",
|
||||
text: AppLocalizations.of(context)!.save,
|
||||
icon: Icons.done,
|
||||
color: kSuccess,
|
||||
textColor: Colors.white,
|
||||
@ -432,14 +433,14 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
|
||||
Future<void> delete(ConfigurationDTO configurationDTO, AppContext appContext) async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir supprimer cette visite ?",
|
||||
AppLocalizations.of(context)!.configDeleteConfirm,
|
||||
() {},
|
||||
() async {
|
||||
ManagerAppContext managerAppContext = appContext.getContext();
|
||||
await managerAppContext.clientAPI!.configurationApi!.configurationDelete(configurationDTO.id!);
|
||||
managerAppContext.selectedConfiguration = null;
|
||||
appContext.setContext(managerAppContext);
|
||||
showNotification(kSuccess, kWhite, 'La configuration a été supprimée avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configDeletedSuccess, context, null);
|
||||
},
|
||||
context
|
||||
);
|
||||
@ -451,7 +452,7 @@ class _ConfigurationDetailScreenState extends State<ConfigurationDetailScreen> {
|
||||
managerAppContext.selectedConfiguration = configuration;
|
||||
appContext.setContext(managerAppContext);
|
||||
|
||||
showNotification(kSuccess, kWhite, 'La configuration a été sauvegardée avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configSavedSuccess, context, null);
|
||||
}
|
||||
|
||||
Future<ConfigurationDTO?> getConfiguration(ConfigurationDetailScreen widget, Client client) async {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Screens/Configurations/configuration_detail_screen.dart';
|
||||
@ -46,7 +47,7 @@ class _ConfigurationsScreenState extends State<ConfigurationsScreen> {
|
||||
if (managerAppContext.canEdit) tempOutput.add(ConfigurationDTO(id: null));
|
||||
return bodyGrid(tempOutput, size, appContext, context);
|
||||
} else if (snapshot.connectionState == ConnectionState.none) {
|
||||
return Text("No data");
|
||||
return Text(AppLocalizations.of(context)!.noData);
|
||||
} else {
|
||||
return Center(
|
||||
child: Container(
|
||||
@ -96,7 +97,7 @@ class _ConfigurationsScreenState extends State<ConfigurationsScreen> {
|
||||
managerAppContext.selectedConfiguration = null;
|
||||
await appContext.setContext(managerAppContext);
|
||||
|
||||
showNotification(kSuccess, kWhite, 'La configuration a été créée avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.configCreatedSuccess, context, null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
//import 'package:filepicker_windows/filepicker_windows.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Components/string_input_container.dart';
|
||||
@ -11,6 +12,7 @@ import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChanged<bool> isImport, BuildContext context, BuildContext mainContext) {
|
||||
final l = AppLocalizations.of(mainContext)!;
|
||||
ConfigurationDTO configurationDTO = new ConfigurationDTO();
|
||||
Size size = MediaQuery.of(mainContext).size;
|
||||
configurationDTO.label = "";
|
||||
@ -28,12 +30,12 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Center(child: Text("Nouvelle configuration", style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400))),
|
||||
Center(child: Text(l.newConfiguration, style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400))),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
child: StringInputContainer(
|
||||
label: "Nom :",
|
||||
label: l.configNameLabel,
|
||||
initialValue: configurationDTO.label,
|
||||
onChanged: (value) {
|
||||
configurationDTO.label = value;
|
||||
@ -41,12 +43,12 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
|
||||
),
|
||||
),
|
||||
),
|
||||
Text("ou"),
|
||||
Text(l.orText),
|
||||
Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 5.0),
|
||||
child: Text("Importer", style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300)),
|
||||
child: Text(l.importLabel, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300)),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
@ -85,7 +87,7 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
|
||||
width: 175,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Annuler",
|
||||
text: l.cancel,
|
||||
icon: Icons.undo,
|
||||
color: kSecond,
|
||||
press: () {
|
||||
@ -101,7 +103,7 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
|
||||
width: 150,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Créer",
|
||||
text: l.create,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
@ -110,7 +112,7 @@ Future<ConfigurationDTO?> showNewConfiguration(AppContext appContext, ValueChang
|
||||
//create(configurationDTO, appContext, context);
|
||||
Navigator.of(context).pop(configurationDTO);
|
||||
} else {
|
||||
showNotification(Colors.orange, kWhite, 'Veuillez spécifier un nom pour la nouvelle visite', context, null);
|
||||
showNotification(Colors.orange, kWhite, l.configNameRequired, context, null);
|
||||
}
|
||||
},
|
||||
fontSize: 20,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/Components/dropDown_input_container.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Components/string_input_container.dart';
|
||||
@ -10,6 +11,7 @@ import 'package:manager_api_new/api.dart';
|
||||
|
||||
Future<SectionDTO?> showNewSection(String configurationId,
|
||||
AppContext appContext, BuildContext contextBuild, bool isSubSection) {
|
||||
final l = AppLocalizations.of(contextBuild)!;
|
||||
SectionDTO sectionDTO = new SectionDTO();
|
||||
sectionDTO.label = "";
|
||||
sectionDTO.configurationId = configurationId;
|
||||
@ -18,7 +20,6 @@ Future<SectionDTO?> showNewSection(String configurationId,
|
||||
sectionDTO.parentId = isSubSection
|
||||
? (appContext.getContext() as ManagerAppContext).selectedSection!.id
|
||||
: null;
|
||||
Size size = MediaQuery.of(contextBuild).size;
|
||||
sectionDTO.type = SectionType.Map;
|
||||
|
||||
var section = showDialog<SectionDTO?>(
|
||||
@ -35,8 +36,8 @@ Future<SectionDTO?> showNewSection(String configurationId,
|
||||
children: [
|
||||
Text(
|
||||
isSubSection
|
||||
? "Nouvelle sous section"
|
||||
: "Nouvelle section",
|
||||
? l.newSubSection
|
||||
: l.newSection,
|
||||
style: new TextStyle(
|
||||
fontSize: 25, fontWeight: FontWeight.w400)),
|
||||
Column(
|
||||
@ -44,7 +45,7 @@ Future<SectionDTO?> showNewSection(String configurationId,
|
||||
SizedBox(
|
||||
height: 100,
|
||||
child: StringInputContainer(
|
||||
label: "Nom :",
|
||||
label: l.sectionNameLabel,
|
||||
initialValue: sectionDTO.label,
|
||||
onChanged: (value) {
|
||||
sectionDTO.label = value;
|
||||
@ -52,7 +53,7 @@ Future<SectionDTO?> showNewSection(String configurationId,
|
||||
),
|
||||
),
|
||||
DropDownInputContainer(
|
||||
label: "Type:",
|
||||
label: l.sectionTypeLabel,
|
||||
values: isSubSection
|
||||
? section_types
|
||||
.where(
|
||||
@ -92,7 +93,7 @@ Future<SectionDTO?> showNewSection(String configurationId,
|
||||
width: 175,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Annuler",
|
||||
text: l.cancel,
|
||||
icon: Icons.undo,
|
||||
color: kSecond,
|
||||
press: () {
|
||||
@ -108,7 +109,7 @@ Future<SectionDTO?> showNewSection(String configurationId,
|
||||
width: 150,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Créer",
|
||||
text: l.create,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
@ -122,7 +123,7 @@ Future<SectionDTO?> showNewSection(String configurationId,
|
||||
showNotification(
|
||||
Colors.orange,
|
||||
kWhite,
|
||||
'Veuillez spécifier un nom pour la nouvelle section',
|
||||
l.sectionNameRequired,
|
||||
context,
|
||||
null);
|
||||
}
|
||||
@ -156,7 +157,7 @@ void create(SectionDTO sectionDTO, AppContext appContext, BuildContext context,
|
||||
}
|
||||
managerAppContext.selectedConfiguration.sectionIds.add(newSection.id);*/
|
||||
appContext.setContext(managerAppContext);
|
||||
showNotification(kSuccess, kWhite, 'La section a été créée avec succès !',
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.sectionCreatedSuccess,
|
||||
context, null);
|
||||
} else {
|
||||
sendSubSection!(newSection);
|
||||
|
||||
@ -9,6 +9,7 @@ import 'package:manager_app/Components/string_input_container.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
import '../../Components/resource_input_container.dart';
|
||||
@ -73,7 +74,7 @@ showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appC
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Text("Aucune configuration trouvée");
|
||||
return Text(AppLocalizations.of(context)!.noConfigFound);
|
||||
}
|
||||
|
||||
} else if (snapshot.connectionState == ConnectionState.none) {
|
||||
@ -104,7 +105,7 @@ showChangeInfo (String text, DeviceDTO inputDevice, AppConfigurationLinkDTO appC
|
||||
},
|
||||
),
|
||||
ColorPickerInputContainer(
|
||||
label: "Couleur fond d'écran :",
|
||||
label: AppLocalizations.of(context)!.backgroundColorLabel,
|
||||
fontSize: 20,
|
||||
color: appConfiguration.secondaryColor,
|
||||
onChanged: (value) {
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Screens/Kiosk_devices/change_device_info_modal.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -85,7 +86,7 @@ class _DeviceElementState extends State<DeviceElement> {
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
showChangeInfo(
|
||||
"Mettre à jour la tablette",
|
||||
AppLocalizations.of(context)!.updateTabletBtn,
|
||||
widget.deviceDTO,
|
||||
widget.appConfigurationLinkDTO,
|
||||
(DeviceDTO outputDevice, AppConfigurationLinkDTO outputAppConfig) async {
|
||||
@ -120,7 +121,7 @@ class _DeviceElementState extends State<DeviceElement> {
|
||||
AppConfigurationLinkDTO? result = await managerAppContext.clientAPI!.applicationInstanceApi!.applicationInstanceUpdateApplicationLink(appConfigurationToUpdate);
|
||||
|
||||
//print(device);
|
||||
showNotification(kSuccess, kWhite, "Le kiosk a été mis à jour", context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.kioskUpdatedSuccess, context, null);
|
||||
return device;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Screens/Applications/app_configuration_link_screen.dart';
|
||||
@ -35,7 +36,7 @@ class _KioskScreenState extends State<KioskScreen> {
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: AutoSizeText(
|
||||
"Code pin: " + managerAppContext.pinCode.toString(),
|
||||
AppLocalizations.of(context)!.pinCode(managerAppContext.pinCode.toString()),
|
||||
style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.w300),
|
||||
maxLines: 2,
|
||||
maxFontSize: 25.0,
|
||||
@ -49,7 +50,7 @@ class _KioskScreenState extends State<KioskScreen> {
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text("Tablettes"),
|
||||
child: Text(AppLocalizations.of(context)!.tablets),
|
||||
)
|
||||
),
|
||||
FutureBuilder(
|
||||
@ -179,7 +180,7 @@ class _KioskScreenState extends State<KioskScreen> {
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
showSelectConfigModal(
|
||||
"Sélectionner une configuration",
|
||||
AppLocalizations.of(context)!.selectConfiguration,
|
||||
(String configurationId) {
|
||||
|
||||
device.configurationId = configurationId;
|
||||
@ -201,7 +202,7 @@ class _KioskScreenState extends State<KioskScreen> {
|
||||
) : InkWell(
|
||||
onTap: () {
|
||||
showSelectConfigModal(
|
||||
"Sélectionner une configuration",
|
||||
AppLocalizations.of(context)!.selectConfiguration,
|
||||
(String configurationId) {
|
||||
|
||||
device.configurationId = configurationId;
|
||||
@ -226,7 +227,7 @@ class _KioskScreenState extends State<KioskScreen> {
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
|
||||
child: Center(
|
||||
child: AutoSizeText(
|
||||
"Sélectionner",
|
||||
AppLocalizations.of(context)!.choose,
|
||||
style: TextStyle(color: kWhite),
|
||||
maxLines: 1,
|
||||
)
|
||||
|
||||
@ -16,7 +16,9 @@ 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';
|
||||
@ -81,6 +83,100 @@ class _MainScreenState extends State<MainScreen> {
|
||||
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 {
|
||||
@ -94,11 +190,11 @@ class _MainScreenState extends State<MainScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Changer d\'instance'),
|
||||
title: Text(AppLocalizations.of(context)!.switchInstance),
|
||||
content: SizedBox(
|
||||
width: 400,
|
||||
child: instanceList.isEmpty
|
||||
? const Text('Aucune instance trouvée')
|
||||
? Text(AppLocalizations.of(context)!.noInstanceFound)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: instanceList.length,
|
||||
@ -117,7 +213,19 @@ class _MainScreenState extends State<MainScreen> {
|
||||
fontWeight: isCurrent ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
trailing: isCurrent ? const Icon(Icons.check, color: Colors.green) : null,
|
||||
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 {
|
||||
@ -141,12 +249,26 @@ class _MainScreenState extends State<MainScreen> {
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(context, rootNavigator: true).pop(), child: const Text('Fermer')),
|
||||
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;
|
||||
@ -228,7 +350,7 @@ class _MainScreenState extends State<MainScreen> {
|
||||
color: currentPath.contains(section.type) ? kPrimaryColor : kBodyTextColor,
|
||||
size: 22,
|
||||
),
|
||||
title: Text(section.name, style: TextStyle(color: currentPath.contains(section.type) ? kPrimaryColor : kBodyTextColor, fontSize: 22, fontWeight: currentPath.contains(section.type) ? FontWeight.w500 : FontWeight.w100)),
|
||||
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((_) =>
|
||||
@ -252,7 +374,7 @@ class _MainScreenState extends State<MainScreen> {
|
||||
),
|
||||
iconColor: isAppActive ? kPrimaryColor : kBodyTextColor,
|
||||
collapsedIconColor: isAppActive ? kPrimaryColor : kBodyTextColor,
|
||||
title: Text(section.name, style: TextStyle(color: isAppActive ? kPrimaryColor : kBodyTextColor, fontSize: 22, fontWeight: isAppActive ? FontWeight.w500 : FontWeight.w100)),
|
||||
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)
|
||||
@ -276,7 +398,7 @@ class _MainScreenState extends State<MainScreen> {
|
||||
subSection.type == "vr" ? Icon(Icons.panorama_photosphere, color: currentPath.contains(subSection.type)? kPrimaryColor : kBodyTextColor, size: 20) : SizedBox(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(subSection.name, style: TextStyle(color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, fontSize: 18)),
|
||||
child: Text(_localizedSectionName(context, subSection.type), style: TextStyle(color: currentPath.contains(subSection.type) ? kPrimaryColor : kBodyTextColor, fontSize: 18)),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -302,11 +424,26 @@ class _MainScreenState extends State<MainScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// Footer: Email + Switch instance (SuperAdmin) + Logout
|
||||
// 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"),
|
||||
@ -315,7 +452,7 @@ class _MainScreenState extends State<MainScreen> {
|
||||
if ((appContext.getContext() as ManagerAppContext).role == UserRole.SuperAdmin)
|
||||
IconButton(
|
||||
icon: Icon(Icons.swap_horiz, color: kPrimaryColor),
|
||||
tooltip: 'Changer d\'instance',
|
||||
tooltip: AppLocalizations.of(context)!.tooltipSwitchInstance,
|
||||
onPressed: () => _showSwitchInstanceDialog(context, appContext.getContext() as ManagerAppContext),
|
||||
),
|
||||
IconButton(
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -17,17 +19,15 @@ class NotificationsScreen extends StatefulWidget {
|
||||
class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
List<PushNotificationDTO> _notifications = [];
|
||||
bool _loading = true;
|
||||
String? _statusFilter; // null = toutes
|
||||
String? _statusFilter;
|
||||
int _page = 0;
|
||||
static const _pageSize = 15;
|
||||
|
||||
// ─── KPI counts ───────────────────────────────────────────
|
||||
int get _totalCount => _notifications.length;
|
||||
int get _sentCount => _notifications.where((n) => n.status == 'Sent').length;
|
||||
int get _scheduledCount => _notifications.where((n) => n.status == 'Scheduled').length;
|
||||
int get _failedCount => _notifications.where((n) => n.status == 'Failed').length;
|
||||
|
||||
// ─── Filtre + pagination ───────────────────────────────────
|
||||
List<PushNotificationDTO> get _filtered => _statusFilter == null
|
||||
? _notifications
|
||||
: _notifications.where((n) => n.status == _statusFilter).toList();
|
||||
@ -43,7 +43,6 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
// ─── API helpers ──────────────────────────────────────────
|
||||
Future<void> _load(ManagerAppContext ctx) async {
|
||||
try {
|
||||
final response = await ctx.clientAPI!.notificationApi!.notificationGetWithHttpInfo();
|
||||
@ -78,8 +77,8 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
return response.statusCode == 202;
|
||||
}
|
||||
|
||||
// ─── Dialogs ──────────────────────────────────────────────
|
||||
void _showSendModal(BuildContext context, ManagerAppContext ctx) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final titleCtrl = TextEditingController();
|
||||
final bodyCtrl = TextEditingController();
|
||||
bool isScheduled = false;
|
||||
@ -90,18 +89,18 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
context: context,
|
||||
builder: (_) => StatefulBuilder(builder: (ctx2, setLocal) {
|
||||
return AlertDialog(
|
||||
title: const Text('Nouveau message'),
|
||||
title: Text(l.newMessage),
|
||||
content: SizedBox(
|
||||
width: 480,
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
TextField(
|
||||
controller: titleCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Titre'),
|
||||
decoration: InputDecoration(labelText: l.messageTitle),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: bodyCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Message'),
|
||||
decoration: InputDecoration(labelText: l.messageBody),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@ -111,7 +110,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
value: isScheduled,
|
||||
onChanged: (v) => setLocal(() => isScheduled = v ?? false),
|
||||
),
|
||||
const Text('Planifier'),
|
||||
Text(l.schedule),
|
||||
],
|
||||
),
|
||||
if (isScheduled) ...[
|
||||
@ -122,7 +121,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
icon: const Icon(Icons.calendar_today, size: 16),
|
||||
label: Text(scheduledDate != null
|
||||
? '${scheduledDate!.day.toString().padLeft(2, '0')}/${scheduledDate!.month.toString().padLeft(2, '0')}/${scheduledDate!.year}'
|
||||
: 'Date'),
|
||||
: l.date),
|
||||
onPressed: () async {
|
||||
final d = await showDatePicker(
|
||||
context: ctx2,
|
||||
@ -140,7 +139,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
icon: const Icon(Icons.access_time, size: 16),
|
||||
label: Text(scheduledTime != null
|
||||
? scheduledTime!.format(ctx2)
|
||||
: 'Heure'),
|
||||
: l.time),
|
||||
onPressed: () async {
|
||||
final t = await showTimePicker(
|
||||
context: ctx2,
|
||||
@ -157,7 +156,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx2),
|
||||
child: const Text('Annuler'),
|
||||
child: Text(l.cancel),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: titleCtrl.text.isEmpty ? null : () async {
|
||||
@ -179,7 +178,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
await _load(ctx);
|
||||
}
|
||||
},
|
||||
child: const Text('Envoyer'),
|
||||
child: Text(l.send),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -188,13 +187,14 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
}
|
||||
|
||||
void _confirmCancel(BuildContext context, ManagerAppContext ctx, PushNotificationDTO notif) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Annuler la notification'),
|
||||
content: Text('Annuler « ${notif.title} » ?'),
|
||||
title: Text(l.cancelNotification),
|
||||
content: Text(l.cancelNotificationConfirm(notif.title ?? '')),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(context), child: const Text('Non')),
|
||||
TextButton(onPressed: () => Navigator.pop(context), child: Text(l.no)),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||||
onPressed: () async {
|
||||
@ -202,14 +202,13 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
final ok = await _cancel(ctx, notif.id!);
|
||||
if (ok && context.mounted) await _load(ctx);
|
||||
},
|
||||
child: const Text('Annuler', style: TextStyle(color: Colors.white)),
|
||||
child: Text(l.cancel, style: const TextStyle(color: Colors.white)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────
|
||||
static String _formatDate(DateTime? dt) {
|
||||
if (dt == null) return '—';
|
||||
final d = dt.toLocal();
|
||||
@ -245,7 +244,6 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Build ────────────────────────────────────────────────
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -257,32 +255,30 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final appContext = Provider.of<AppContext>(context);
|
||||
final ctx = appContext.getContext() as ManagerAppContext;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// ── Header ──
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Notifications',
|
||||
Text(l.notificationsTitle,
|
||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _showSendModal(context, ctx),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Nouveau message'),
|
||||
label: Text(l.newMessage),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// ── KPIs ──
|
||||
Row(
|
||||
children: [
|
||||
_KpiCard(
|
||||
label: 'Toutes',
|
||||
label: l.allNotifications,
|
||||
count: _totalCount,
|
||||
color: Colors.blueGrey,
|
||||
selected: _statusFilter == null,
|
||||
@ -290,7 +286,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_KpiCard(
|
||||
label: 'Envoyées',
|
||||
label: l.sentNotifications,
|
||||
count: _sentCount,
|
||||
color: Colors.green,
|
||||
selected: _statusFilter == 'Sent',
|
||||
@ -298,7 +294,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_KpiCard(
|
||||
label: 'Planifiées',
|
||||
label: l.scheduledNotifications,
|
||||
count: _scheduledCount,
|
||||
color: Colors.blue,
|
||||
selected: _statusFilter == 'Scheduled',
|
||||
@ -306,7 +302,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_KpiCard(
|
||||
label: 'Échouées',
|
||||
label: l.failedNotifications,
|
||||
count: _failedCount,
|
||||
color: Colors.red,
|
||||
selected: _statusFilter == 'Failed',
|
||||
@ -315,14 +311,12 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// ── List ──
|
||||
if (_loading)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
const CommonLoader()
|
||||
else if (_notifications.isEmpty)
|
||||
const Center(child: Text('Aucune notification envoyée'))
|
||||
Center(child: Text(l.noNotifications))
|
||||
else if (_filtered.isEmpty)
|
||||
const Center(child: Text('Aucune notification pour ce filtre'))
|
||||
Center(child: Text(l.noNotificationsForFilter))
|
||||
else
|
||||
Expanded(
|
||||
child: Card(
|
||||
@ -344,12 +338,12 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
columnSpacing: 24,
|
||||
headingRowColor: WidgetStateProperty.all(Colors.grey.shade50),
|
||||
dividerThickness: 1,
|
||||
columns: const [
|
||||
DataColumn(label: Text('Titre', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Topic', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Date', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Statut', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('')),
|
||||
columns: [
|
||||
DataColumn(label: Text(l.messageTitle, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.topic, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.date, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.status, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
const DataColumn(label: Text('')),
|
||||
],
|
||||
rows: _paginated.map((n) {
|
||||
final isScheduled = n.status == 'Scheduled';
|
||||
@ -362,7 +356,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
DataCell(isScheduled
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.delete_outline, color: Colors.red, size: 20),
|
||||
tooltip: 'Annuler',
|
||||
tooltip: l.tooltipCancelNotification,
|
||||
onPressed: () => _confirmCancel(context, ctx, n),
|
||||
)
|
||||
: const SizedBox()),
|
||||
@ -372,7 +366,6 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// ── Pagination ──
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(top: BorderSide(color: Colors.grey.shade200)),
|
||||
@ -392,7 +385,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'${_filtered.length} résultat${_filtered.length > 1 ? 's' : ''}',
|
||||
l.resultsCount(_filtered.length),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
|
||||
@ -3,6 +3,7 @@ import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
|
||||
class WebView extends StatefulWidget {
|
||||
WebView({required this.htmlText});
|
||||
@ -63,5 +64,5 @@ class _WebViewWidget extends State<WebView> {
|
||||
) :
|
||||
//WebViewWidget(controller: controller!)
|
||||
Text("Texte"):
|
||||
Center(child: Text("La page internet ne peut pas être affichée, l'url est incorrecte ou vide"));
|
||||
Center(child: Text(AppLocalizations.of(context)!.webViewError));
|
||||
} //_webView
|
||||
@ -1,6 +1,7 @@
|
||||
|
||||
import 'package:manager_app/Components/audio_player.dart';
|
||||
import 'package:manager_app/Components/video_viewer.dart';
|
||||
import 'package:manager_app/Components/video_viewer_youtube.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
@ -87,14 +88,11 @@ getElementForResource(dynamic resourceDTO, AppContext appContext) {
|
||||
}
|
||||
|
||||
case ResourceType.VideoUrl:
|
||||
return SelectableText(resourceDTO.url!);
|
||||
|
||||
/*case ResourceType.VideoUrl:
|
||||
if(resourceDTO.url == null) {
|
||||
return Center(child: Text("Error loading video"));
|
||||
} else {
|
||||
return VideoViewerYoutube(videoUrl: resourceDTO.url!);
|
||||
}*/ // TODO
|
||||
}
|
||||
|
||||
case ResourceType.Pdf:
|
||||
return Text("Fichier pdf - aucune visualisation possible");
|
||||
|
||||
@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
|
||||
dynamic showNewResource(AppContext appContext, BuildContext context) async {
|
||||
@ -27,7 +28,7 @@ dynamic showNewResource(AppContext appContext, BuildContext context) async {
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Text("Nouvelle ressource", style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)),
|
||||
Text(AppLocalizations.of(context)!.newResource, style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)),
|
||||
/*Column(
|
||||
children: [
|
||||
StringInputContainer(
|
||||
@ -70,7 +71,7 @@ dynamic showNewResource(AppContext appContext, BuildContext context) async {
|
||||
width: 175,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Annuler",
|
||||
text: AppLocalizations.of(context)!.cancel,
|
||||
icon: Icons.undo,
|
||||
color: kSecond,
|
||||
press: () {
|
||||
@ -86,7 +87,7 @@ dynamic showNewResource(AppContext appContext, BuildContext context) async {
|
||||
width: 150,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Créer",
|
||||
text: AppLocalizations.of(context)!.create,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
textColor: kWhite,
|
||||
@ -96,13 +97,13 @@ dynamic showNewResource(AppContext appContext, BuildContext context) async {
|
||||
if(resourceDetailDTO.url != null || filesToSendWeb != null) { // TODO clarify resourceDetailDTO.data != null
|
||||
Navigator.pop(context, [resourceDetailDTO, filesToSend, filesToSendWeb]);
|
||||
} else {
|
||||
showNotification(Colors.orange, kWhite, 'Aucun fichier n\'a été chargé', context, null);
|
||||
showNotification(Colors.orange, kWhite, AppLocalizations.of(context)!.resourceNoFileLoaded, context, null);
|
||||
}
|
||||
} else {
|
||||
if (resourceDetailDTO.url != null || filesToSendWeb!.length > 0 || filesToSend!.length > 0) { // TODO clarify resourceDetailDTO.data != null
|
||||
Navigator.pop(context, [resourceDetailDTO, filesToSend, filesToSendWeb]);
|
||||
} else {
|
||||
showNotification(Colors.orange, kWhite, 'Aucun fichier n\'a été chargé', context, null);
|
||||
showNotification(Colors.orange, kWhite, AppLocalizations.of(context)!.resourceNoFileLoaded, context, null);
|
||||
}
|
||||
}
|
||||
/*} else {
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import 'dart:io';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Screens/Resources/new_resource_popup.dart';
|
||||
import 'package:manager_app/Screens/Resources/resource_body_grid.dart';
|
||||
@ -103,7 +106,7 @@ class _ResourcesScreenState extends State<ResourcesScreen> {
|
||||
var resourceUpdated = managerAppContext.clientAPI!.resourceApi!.resourceUpdate(result);
|
||||
setState(() {
|
||||
// refresh ui
|
||||
showNotification(kSuccess, kWhite, 'La ressource a été mise à jour avec succès', context, null);
|
||||
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.resourceUpdatedSuccess, context, null);
|
||||
});
|
||||
} catch (e) {
|
||||
print("Error during updating resource");
|
||||
@ -117,10 +120,10 @@ class _ResourcesScreenState extends State<ResourcesScreen> {
|
||||
}
|
||||
});//bodyGrid(tempOutput, size, appContext);
|
||||
} else {
|
||||
return Text("No data");
|
||||
return Text(AppLocalizations.of(context)!.noData);
|
||||
}
|
||||
} else if (snapshot.connectionState == ConnectionState.none) {
|
||||
return Text("No data");
|
||||
return Text(AppLocalizations.of(context)!.noData);
|
||||
} else {
|
||||
return Center(
|
||||
child: Container(
|
||||
@ -149,6 +152,24 @@ Future<List<ResourceDTO?>?> create(ResourceDTO resourceDTO, List<File>? files, L
|
||||
int index = 0;
|
||||
|
||||
if(filesWeb != null) {
|
||||
// Vérification quota stockage avant upload
|
||||
if ((managerAppContext.instanceDTO?.storageQuotaBytes ?? 0) > 0) {
|
||||
try {
|
||||
final quotaUri = Uri.parse('${managerAppContext.host}/api/Instance/${managerAppContext.instanceId}/quota');
|
||||
final quotaResponse = await http.get(quotaUri, headers: {'Authorization': 'Bearer ${managerAppContext.accessToken}'});
|
||||
if (quotaResponse.statusCode == 200) {
|
||||
final quotaData = jsonDecode(quotaResponse.body);
|
||||
final storageUsed = (quotaData['storageUsedBytes'] as num).toInt();
|
||||
final storageQuota = (quotaData['storageQuotaBytes'] as num).toInt();
|
||||
final incomingBytes = filesWeb.fold<int>(0, (sum, f) => sum + (f.size));
|
||||
if (storageUsed + incomingBytes > storageQuota) {
|
||||
showNotification(kError, kWhite, 'Quota de stockage dépassé', context, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
for (PlatformFile platformFile in filesWeb) {
|
||||
var mimeType = "";
|
||||
ResourceDTO resourceDTO = new ResourceDTO(label: platformFile.name);
|
||||
@ -200,8 +221,9 @@ Future<List<ResourceDTO?>?> create(ResourceDTO resourceDTO, List<File>? files, L
|
||||
UploadTask uploadTask = ref.putData(platformFile.bytes!, metadata);
|
||||
uploadTask.then((res) {
|
||||
res.ref.getDownloadURL().then((urlRessource) {
|
||||
showNotification(Colors.green, kWhite, 'La ressource a été créée avec succès', context, null);
|
||||
showNotification(Colors.green, kWhite, AppLocalizations.of(context)!.resourceCreatedSuccess, context, null);
|
||||
newResource.url = urlRessource;
|
||||
newResource.sizeBytes = platformFile.size;
|
||||
(appContext.getContext() as ManagerAppContext).clientAPI!.resourceApi!.resourceUpdate(newResource);
|
||||
createdResources.add(newResource);
|
||||
index++;
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:manager_app/Components/string_input_container.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'dart:html' as html;
|
||||
|
||||
@ -110,7 +111,7 @@ Future<ResourceDTO?> showResource(ResourceDTO resourceDTO, AppContext appContext
|
||||
width: 220,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Télécharger",
|
||||
text: AppLocalizations.of(context)!.download,
|
||||
icon: Icons.download,
|
||||
color: kPrimaryColor,
|
||||
press: () {
|
||||
@ -134,7 +135,7 @@ Future<ResourceDTO?> showResource(ResourceDTO resourceDTO, AppContext appContext
|
||||
width: 220,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Sauvegarder",
|
||||
text: AppLocalizations.of(context)!.save,
|
||||
icon: Icons.check,
|
||||
color: kPrimaryColor,
|
||||
press: () {
|
||||
@ -157,7 +158,7 @@ Future<ResourceDTO?> showResource(ResourceDTO resourceDTO, AppContext appContext
|
||||
|
||||
Future<void> delete(ResourceDTO resourceDTO, AppContext appContext, context) async {
|
||||
showConfirmationDialog(
|
||||
"Êtes-vous sûr de vouloir supprimer cette ressource ?",
|
||||
AppLocalizations.of(context)!.resourceDeleteConfirm,
|
||||
() {},
|
||||
() async {
|
||||
await (appContext.getContext() as ManagerAppContext).clientAPI!.resourceApi!.resourceDelete(resourceDTO.id!);
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -55,20 +57,20 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(),
|
||||
_buildHeader(context),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: FutureBuilder<StatsSummaryDTO?>(
|
||||
future: _future,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
return const CommonLoader();
|
||||
}
|
||||
if (snapshot.hasError || snapshot.data == null) {
|
||||
return Center(child: Text('Impossible de charger les statistiques', style: TextStyle(color: kBodyTextColor)));
|
||||
return Center(child: Text(AppLocalizations.of(context)!.statsLoadError, style: TextStyle(color: kBodyTextColor)));
|
||||
}
|
||||
final stats = snapshot.data!;
|
||||
return _buildDashboard(stats);
|
||||
return _buildDashboard(context, stats);
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -77,23 +79,33 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
bool get _hasAdvancedStats =>
|
||||
_managerAppContext.instanceDTO?.hasAdvancedStats ?? false;
|
||||
|
||||
int get _statsHistoryDays =>
|
||||
_managerAppContext.instanceDTO?.statsHistoryDays ?? 30;
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final historyDays = _statsHistoryDays;
|
||||
final maxDays = historyDays == 0 ? 999 : historyDays;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Statistiques', style: TextStyle(fontSize: 26, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
Text(l.statisticsTitle, style: TextStyle(fontSize: 26, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
SegmentedButton<int>(
|
||||
style: SegmentedButton.styleFrom(
|
||||
selectedBackgroundColor: kPrimaryColor,
|
||||
selectedForegroundColor: kWhite,
|
||||
),
|
||||
segments: const [
|
||||
ButtonSegment(value: 7, label: Text('7j')),
|
||||
ButtonSegment(value: 30, label: Text('30j')),
|
||||
ButtonSegment(value: 90, label: Text('90j')),
|
||||
segments: [
|
||||
const ButtonSegment(value: 7, label: Text('7j')),
|
||||
const ButtonSegment(value: 30, label: Text('30j')),
|
||||
ButtonSegment(value: 90, label: Text('90j'), enabled: maxDays >= 90),
|
||||
],
|
||||
selected: {_selectedDays},
|
||||
onSelectionChanged: (s) {
|
||||
@ -108,7 +120,7 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
spacing: 8,
|
||||
children: [
|
||||
ChoiceChip(
|
||||
label: const Text('Tous'),
|
||||
label: Text(l.statsAll),
|
||||
selected: _selectedAppType == null,
|
||||
selectedColor: kPrimaryColor,
|
||||
labelStyle: TextStyle(color: _selectedAppType == null ? kWhite : kBodyTextColor),
|
||||
@ -127,7 +139,8 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDashboard(StatsSummaryDTO stats) {
|
||||
Widget _buildDashboard(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
if (stats.totalSessions == 0) {
|
||||
return Center(
|
||||
child: Column(
|
||||
@ -136,13 +149,13 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
Icon(Icons.bar_chart_outlined, size: 56, color: kBodyTextColor.withValues(alpha: 0.4)),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Pas encore de données pour cette période',
|
||||
l.statsNoData,
|
||||
style: TextStyle(fontSize: 15, color: kBodyTextColor),
|
||||
),
|
||||
if (_selectedAppType != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Aucun event reçu pour le type "${_selectedAppType!.name}"',
|
||||
l.statsNoDataForType(_selectedAppType!.name),
|
||||
style: TextStyle(fontSize: 13, color: kBodyTextColor.withValues(alpha: 0.6)),
|
||||
),
|
||||
],
|
||||
@ -155,23 +168,20 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// KPI cards
|
||||
_buildKpiRow(stats),
|
||||
_buildKpiRow(context, stats),
|
||||
const SizedBox(height: 20),
|
||||
// Line chart
|
||||
_buildLineChart(stats),
|
||||
_buildLineChart(context, stats),
|
||||
const SizedBox(height: 20),
|
||||
// Bar chart + Donut charts
|
||||
_buildChartsRow(stats),
|
||||
_buildChartsRow(context, stats),
|
||||
const SizedBox(height: 20),
|
||||
// Bottom tables
|
||||
_buildTablesRow(stats),
|
||||
_buildTablesRow(context, stats),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildKpiRow(StatsSummaryDTO stats) {
|
||||
Widget _buildKpiRow(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final topAppType = stats.appTypeDistribution.isNotEmpty
|
||||
? stats.appTypeDistribution.entries.reduce((a, b) => a.value > b.value ? a : b)
|
||||
: null;
|
||||
@ -181,13 +191,13 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
_kpiCard('Sessions', '${stats.totalSessions}', Icons.people_outline),
|
||||
_kpiCard(l.statsSessions, '${stats.totalSessions}', Icons.people_outline),
|
||||
const SizedBox(width: 12),
|
||||
_kpiCard('Durée moy.', _formatDuration(stats.avgVisitDurationSeconds), Icons.timer_outlined),
|
||||
_kpiCard(l.statsAvgDuration, _formatDuration(stats.avgVisitDurationSeconds), Icons.timer_outlined),
|
||||
const SizedBox(width: 12),
|
||||
_kpiCard('App top', topAppType?.key ?? '—', Icons.phone_iphone),
|
||||
_kpiCard(l.statsTopApp, topAppType?.key ?? '—', Icons.phone_iphone),
|
||||
const SizedBox(width: 12),
|
||||
_kpiCard('Langue top', topLang?.key.toUpperCase() ?? '—', Icons.language),
|
||||
_kpiCard(l.statsTopLang, topLang?.key.toUpperCase() ?? '—', Icons.language),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -215,7 +225,8 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLineChart(StatsSummaryDTO stats) {
|
||||
Widget _buildLineChart(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
if (stats.visitsByDay.isEmpty) return const SizedBox();
|
||||
|
||||
final spots = <FlSpot>[];
|
||||
@ -229,7 +240,9 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
tabletSpots.add(FlSpot(i.toDouble(), d.tablet.toDouble()));
|
||||
}
|
||||
|
||||
final isFiltered = _selectedAppType != null;
|
||||
final mobileHasData = mobileSpots.any((s) => s.y > 0);
|
||||
final tabletHasData = tabletSpots.any((s) => s.y > 0);
|
||||
final showBreakdown = _selectedAppType == null && (mobileHasData || tabletHasData);
|
||||
|
||||
return Card(
|
||||
elevation: 0,
|
||||
@ -240,17 +253,17 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Visites par jour', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
Text(l.statsVisitsByDay, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
const SizedBox(height: 4),
|
||||
if (!isFiltered) Row(children: [
|
||||
_legendDot(kPrimaryColor), const SizedBox(width: 4), Text('Mobile', style: TextStyle(fontSize: 12, color: kBodyTextColor)),
|
||||
const SizedBox(width: 12),
|
||||
_legendDot(kSecond), const SizedBox(width: 4), Text('Tablet', style: TextStyle(fontSize: 12, color: kBodyTextColor)),
|
||||
if (showBreakdown) Row(children: [
|
||||
if (mobileHasData) ...[_legendDot(kPrimaryColor), const SizedBox(width: 4), Text('Mobile', style: TextStyle(fontSize: 12, color: kBodyTextColor)), const SizedBox(width: 12)],
|
||||
if (tabletHasData) ...[_legendDot(kSecond), const SizedBox(width: 4), Text('Tablet', style: TextStyle(fontSize: 12, color: kBodyTextColor))],
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: LineChart(LineChartData(
|
||||
minY: 0,
|
||||
gridData: FlGridData(show: true, drawVerticalLine: false),
|
||||
titlesData: FlTitlesData(
|
||||
leftTitles: AxisTitles(sideTitles: SideTitles(showTitles: true, reservedSize: 32)),
|
||||
@ -268,9 +281,12 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
)),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
lineBarsData: isFiltered
|
||||
? [_lineBar(spots, kPrimaryColor)]
|
||||
: [_lineBar(mobileSpots, kPrimaryColor), _lineBar(tabletSpots, kSecond)],
|
||||
lineBarsData: showBreakdown
|
||||
? [
|
||||
if (mobileHasData) _lineBar(mobileSpots, kPrimaryColor),
|
||||
if (tabletHasData) _lineBar(tabletSpots, kSecond),
|
||||
]
|
||||
: [_lineBar(spots, kPrimaryColor)],
|
||||
)),
|
||||
),
|
||||
],
|
||||
@ -289,28 +305,30 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
isCurved: true,
|
||||
color: color,
|
||||
barWidth: 2,
|
||||
dotData: FlDotData(show: false),
|
||||
belowBarData: BarAreaData(show: true, color: color.withOpacity(0.1)),
|
||||
dotData: FlDotData(show: spots.length <= 1),
|
||||
belowBarData: BarAreaData(show: true, color: color.withValues(alpha: 0.1)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildChartsRow(StatsSummaryDTO stats) {
|
||||
Widget _buildChartsRow(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final showAppTypeDonut = _selectedAppType == null;
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(flex: 2, child: _buildTopSectionsChart(stats)),
|
||||
Expanded(flex: 2, child: _buildTopSectionsChart(context, stats)),
|
||||
const SizedBox(width: 12),
|
||||
if (showAppTypeDonut) ...[
|
||||
Expanded(child: _buildDonut(stats.appTypeDistribution, 'Apps')),
|
||||
Expanded(child: _buildDonut(stats.appTypeDistribution, l.statsApps)),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
Expanded(child: _buildDonut(stats.languageDistribution, 'Langues')),
|
||||
Expanded(child: _buildDonut(stats.languageDistribution, l.statsLanguages)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTopSectionsChart(StatsSummaryDTO stats) {
|
||||
Widget _buildTopSectionsChart(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
if (stats.topSections.isEmpty) return const SizedBox();
|
||||
final sections = stats.topSections.take(8).toList();
|
||||
final maxViews = sections.map((s) => s.views).reduce((a, b) => a > b ? a : b);
|
||||
@ -324,7 +342,7 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Top sections', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
Text(l.statsTopSections, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
height: 240,
|
||||
@ -341,7 +359,8 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
getTitlesWidget: (value, meta) {
|
||||
final idx = value.toInt();
|
||||
if (idx < 0 || idx >= sections.length) return const SizedBox();
|
||||
final title = sections[idx].sectionTitle ?? sections[idx].sectionId ?? '';
|
||||
final raw = sections[idx].sectionTitle ?? sections[idx].sectionId ?? '';
|
||||
final title = raw.replaceAll(RegExp(r'<[^>]*>'), '').trim();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
@ -430,15 +449,19 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTablesRow(StatsSummaryDTO stats) {
|
||||
Widget _buildTablesRow(BuildContext context, StatsSummaryDTO stats) {
|
||||
if (!_hasAdvancedStats) {
|
||||
return _buildPremiumLockedCard(context);
|
||||
}
|
||||
|
||||
final tables = <Widget>[
|
||||
if (stats.topPois.isNotEmpty) Expanded(child: _buildPoiTable(stats)),
|
||||
if (stats.topAgendaEvents.isNotEmpty) Expanded(child: _buildAgendaTable(stats)),
|
||||
if (stats.quizStats.isNotEmpty) Expanded(child: _buildQuizTable(stats)),
|
||||
if (stats.gameStats.isNotEmpty) Expanded(child: _buildGameTable(stats)),
|
||||
if (stats.topArticles.isNotEmpty) Expanded(child: _buildArticleTable(stats)),
|
||||
if (stats.topMenuItems.isNotEmpty) Expanded(child: _buildMenuTable(stats)),
|
||||
if (stats.qrScans.totalScans > 0) Expanded(child: _buildQrCard(stats)),
|
||||
if (stats.topPois.isNotEmpty) Expanded(child: _buildPoiTable(context, stats)),
|
||||
if (stats.topAgendaEvents.isNotEmpty) Expanded(child: _buildAgendaTable(context, stats)),
|
||||
if (stats.quizStats.isNotEmpty) Expanded(child: _buildQuizTable(context, stats)),
|
||||
if (stats.gameStats.isNotEmpty) Expanded(child: _buildGameTable(context, stats)),
|
||||
if (stats.topArticles.isNotEmpty) Expanded(child: _buildArticleTable(context, stats)),
|
||||
if (stats.topMenuItems.isNotEmpty) Expanded(child: _buildMenuTable(context, stats)),
|
||||
if (stats.qrScans.totalScans > 0) Expanded(child: _buildQrCard(context, stats)),
|
||||
];
|
||||
if (tables.isEmpty) return const SizedBox();
|
||||
final spaced = <Widget>[];
|
||||
@ -449,51 +472,88 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
return Row(crossAxisAlignment: CrossAxisAlignment.start, children: spaced);
|
||||
}
|
||||
|
||||
Widget _buildPoiTable(StatsSummaryDTO stats) {
|
||||
return _tableCard('Top POI', ['POI', 'Taps'], stats.topPois.map((p) => [
|
||||
Widget _buildPremiumLockedCard(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 0,
|
||||
color: kWhite,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.lock_outline, color: kBodyTextColor.withValues(alpha: 0.5), size: 28),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Statistiques avancées', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Les stats détaillées (POI, quiz, jeux, articles, QR…) sont disponibles avec le plan Premium.',
|
||||
style: TextStyle(fontSize: 13, color: kBodyTextColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPoiTable(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return _tableCard(l.statsTopPOI, [l.statsPOI, l.statsTaps], stats.topPois.map((p) => [
|
||||
p.title ?? p.geoPointId?.toString() ?? '—',
|
||||
'${p.taps}',
|
||||
]).toList());
|
||||
}
|
||||
|
||||
Widget _buildAgendaTable(StatsSummaryDTO stats) {
|
||||
return _tableCard('Top événements agenda', ['Événement', 'Taps'], stats.topAgendaEvents.map((e) => [
|
||||
Widget _buildAgendaTable(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return _tableCard(l.statsTopAgenda, [l.statsEvent, l.statsTaps], stats.topAgendaEvents.map((e) => [
|
||||
e.eventTitle ?? e.eventId ?? '—',
|
||||
'${e.taps}',
|
||||
]).toList());
|
||||
}
|
||||
|
||||
Widget _buildQuizTable(StatsSummaryDTO stats) {
|
||||
return _tableCard('Quiz', ['Section', 'Score moy', 'Complétions'], stats.quizStats.map((q) => [
|
||||
Widget _buildQuizTable(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return _tableCard(l.statsQuiz, [l.statsSection, l.statsAvgScore, l.statsCompletions], stats.quizStats.map((q) => [
|
||||
q.sectionTitle ?? q.sectionId ?? '—',
|
||||
'${q.avgScore.toStringAsFixed(1)} / ${q.totalQuestions}',
|
||||
'${q.completions}',
|
||||
]).toList());
|
||||
}
|
||||
|
||||
Widget _buildGameTable(StatsSummaryDTO stats) {
|
||||
return _tableCard('Jeux', ['Type', 'Complétions', 'Durée moy.'], stats.gameStats.map((g) => [
|
||||
Widget _buildGameTable(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return _tableCard(l.statsGames, [l.statsGameType, l.statsCompletions, l.statsAvgDuration], stats.gameStats.map((g) => [
|
||||
g.gameType ?? '—',
|
||||
'${g.completions}',
|
||||
_formatDuration(g.avgDurationSeconds),
|
||||
]).toList());
|
||||
}
|
||||
|
||||
Widget _buildArticleTable(StatsSummaryDTO stats) {
|
||||
return _tableCard('Articles lus', ['Section', 'Lectures'], stats.topArticles.map((a) => [
|
||||
Widget _buildArticleTable(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return _tableCard(l.statsArticles, [l.statsSection, l.statsReadings], stats.topArticles.map((a) => [
|
||||
a.sectionId ?? '—',
|
||||
'${a.reads}',
|
||||
]).toList());
|
||||
}
|
||||
|
||||
Widget _buildMenuTable(StatsSummaryDTO stats) {
|
||||
return _tableCard('Menu', ['Item', 'Taps'], stats.topMenuItems.map((m) => [
|
||||
Widget _buildMenuTable(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return _tableCard(l.statsMenuTitle, [l.statsMenuItem, l.statsTaps], stats.topMenuItems.map((m) => [
|
||||
m.menuItemTitle ?? m.targetSectionId ?? '—',
|
||||
'${m.taps}',
|
||||
]).toList());
|
||||
}
|
||||
|
||||
Widget _buildQrCard(StatsSummaryDTO stats) {
|
||||
Widget _buildQrCard(BuildContext context, StatsSummaryDTO stats) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final qr = stats.qrScans;
|
||||
return Card(
|
||||
elevation: 0,
|
||||
@ -504,13 +564,13 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('QR Scans', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
Text(l.statsQrScans, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
const SizedBox(height: 12),
|
||||
_qrRow(Icons.qr_code_scanner, 'Total', '${qr.totalScans}', kPrimaryColor),
|
||||
_qrRow(Icons.qr_code_scanner, l.statsTotal, '${qr.totalScans}', kPrimaryColor),
|
||||
const SizedBox(height: 8),
|
||||
_qrRow(Icons.check_circle_outline, 'Valides', '${qr.validScans}', Colors.green.shade600),
|
||||
_qrRow(Icons.check_circle_outline, l.statsValid, '${qr.validScans}', Colors.green.shade600),
|
||||
const SizedBox(height: 8),
|
||||
_qrRow(Icons.cancel_outlined, 'Invalides', '${qr.invalidScans}', Colors.red.shade400),
|
||||
_qrRow(Icons.cancel_outlined, l.statsInvalid, '${qr.invalidScans}', Colors.red.shade400),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/app_context.dart';
|
||||
import 'package:manager_app/Components/common_loader.dart';
|
||||
import 'package:manager_app/constants.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -26,7 +28,6 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
return '—';
|
||||
}
|
||||
|
||||
// Convertit une valeur de rôle (String ou int) en int pour les comparaisons
|
||||
static int _roleToInt(dynamic v) {
|
||||
if (v is int) return v;
|
||||
if (v is String) {
|
||||
@ -36,7 +37,6 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Roles the caller is allowed to assign (can't assign higher than own role)
|
||||
List<int> _allowedRoles(int callerRoleValue) =>
|
||||
[0, 1, 2, 3].where((r) => r >= callerRoleValue).toList();
|
||||
|
||||
@ -88,29 +88,30 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
}
|
||||
|
||||
void _showCreateDialog(BuildContext context, ManagerAppContext ctx) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final callerRole = _roleToInt(ctx.role?.value);
|
||||
final emailCtrl = TextEditingController();
|
||||
final firstCtrl = TextEditingController();
|
||||
final lastCtrl = TextEditingController();
|
||||
final passCtrl = TextEditingController();
|
||||
int selectedRole = callerRole; // default: same as caller
|
||||
int selectedRole = callerRole;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => StatefulBuilder(builder: (ctx2, setLocal) {
|
||||
return AlertDialog(
|
||||
title: const Text('Créer un utilisateur'),
|
||||
title: Text(l.createUserTitle),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
TextField(controller: emailCtrl, decoration: const InputDecoration(labelText: 'Email')),
|
||||
TextField(controller: firstCtrl, decoration: const InputDecoration(labelText: 'Prénom')),
|
||||
TextField(controller: lastCtrl, decoration: const InputDecoration(labelText: 'Nom')),
|
||||
TextField(controller: passCtrl, obscureText: true, decoration: const InputDecoration(labelText: 'Mot de passe')),
|
||||
TextField(controller: emailCtrl, decoration: InputDecoration(labelText: l.email)),
|
||||
TextField(controller: firstCtrl, decoration: InputDecoration(labelText: l.firstName)),
|
||||
TextField(controller: lastCtrl, decoration: InputDecoration(labelText: l.lastName)),
|
||||
TextField(controller: passCtrl, obscureText: true, decoration: InputDecoration(labelText: l.password)),
|
||||
const SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Rôle', style: TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
Text(l.role, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
DropdownButton<int>(
|
||||
value: selectedRole,
|
||||
isExpanded: true,
|
||||
@ -124,14 +125,14 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
]),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(ctx2), child: const Text('Annuler')),
|
||||
TextButton(onPressed: () => Navigator.pop(ctx2), child: Text(l.cancel)),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(ctx2);
|
||||
await _createUser(ctx, emailCtrl.text, firstCtrl.text,
|
||||
lastCtrl.text, passCtrl.text, selectedRole);
|
||||
},
|
||||
child: const Text('Créer'),
|
||||
child: Text(l.create),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -140,6 +141,7 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
}
|
||||
|
||||
void _showEditDialog(BuildContext context, ManagerAppContext ctx, Map<String, dynamic> user) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final callerRole = _roleToInt(ctx.role?.value);
|
||||
final firstCtrl = TextEditingController(text: user['firstName'] as String? ?? '');
|
||||
final lastCtrl = TextEditingController(text: user['lastName'] as String? ?? '');
|
||||
@ -149,16 +151,16 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
context: context,
|
||||
builder: (_) => StatefulBuilder(builder: (ctx2, setLocal) {
|
||||
return AlertDialog(
|
||||
title: const Text('Modifier l\'utilisateur'),
|
||||
title: Text(l.editUserTitle),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
TextField(controller: firstCtrl, decoration: const InputDecoration(labelText: 'Prénom')),
|
||||
TextField(controller: lastCtrl, decoration: const InputDecoration(labelText: 'Nom')),
|
||||
TextField(controller: firstCtrl, decoration: InputDecoration(labelText: l.firstName)),
|
||||
TextField(controller: lastCtrl, decoration: InputDecoration(labelText: l.lastName)),
|
||||
const SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Rôle', style: TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
Text(l.role, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
DropdownButton<int>(
|
||||
value: selectedRole,
|
||||
isExpanded: true,
|
||||
@ -172,14 +174,14 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
]),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(ctx2), child: const Text('Annuler')),
|
||||
TextButton(onPressed: () => Navigator.pop(ctx2), child: Text(l.cancel)),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(ctx2);
|
||||
await _updateUser(ctx, user['id'] as String, firstCtrl.text,
|
||||
lastCtrl.text, selectedRole);
|
||||
},
|
||||
child: const Text('Enregistrer'),
|
||||
child: Text(l.save),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -188,20 +190,21 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
}
|
||||
|
||||
void _confirmDelete(BuildContext context, ManagerAppContext ctx, Map<String, dynamic> user) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Supprimer l\'utilisateur'),
|
||||
content: Text('Supprimer ${user['email']} ?'),
|
||||
title: Text(l.deleteUserTitle),
|
||||
content: Text(l.deleteUserConfirm(user['email'] as String? ?? '')),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(context), child: const Text('Annuler')),
|
||||
TextButton(onPressed: () => Navigator.pop(context), child: Text(l.cancel)),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
await _deleteUser(ctx, user['id'] as String);
|
||||
},
|
||||
child: const Text('Supprimer', style: TextStyle(color: Colors.white)),
|
||||
child: Text(l.delete, style: const TextStyle(color: Colors.white)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -228,6 +231,7 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final appContext = Provider.of<AppContext>(context);
|
||||
final managerCtx = appContext.getContext() as ManagerAppContext;
|
||||
|
||||
@ -237,19 +241,19 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Utilisateurs', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
Text(l.usersTitle, style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: kPrimaryColor)),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _showCreateDialog(context, managerCtx),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Créer un utilisateur'),
|
||||
label: Text(l.createUserBtn),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_loading)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
const CommonLoader()
|
||||
else if (_users.isEmpty)
|
||||
const Center(child: Text('Aucun utilisateur'))
|
||||
Center(child: Text(l.noUsers))
|
||||
else
|
||||
Expanded(
|
||||
child: Card(
|
||||
@ -268,12 +272,12 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
columnSpacing: 24,
|
||||
headingRowColor: WidgetStateProperty.all(Colors.grey.shade50),
|
||||
dividerThickness: 1,
|
||||
columns: const [
|
||||
DataColumn(label: Text('Email', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Prénom', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Nom', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Rôle', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text('Actions', style: TextStyle(fontWeight: FontWeight.w600))),
|
||||
columns: [
|
||||
DataColumn(label: Text(l.email, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.firstName, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.lastName, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.role, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
DataColumn(label: Text(l.actions, style: const TextStyle(fontWeight: FontWeight.w600))),
|
||||
],
|
||||
rows: _users.map((user) {
|
||||
final roleColor = _roleColor(user['role']);
|
||||
@ -294,12 +298,12 @@ class _UsersScreenState extends State<UsersScreen> {
|
||||
DataCell(Row(children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: kPrimaryColor, size: 20),
|
||||
tooltip: 'Modifier',
|
||||
tooltip: l.tooltipEdit,
|
||||
onPressed: () => _showEditDialog(context, managerCtx, user),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete_outline, color: Colors.red, size: 20),
|
||||
tooltip: 'Supprimer',
|
||||
tooltip: l.tooltipDelete,
|
||||
onPressed: () => _confirmDelete(context, managerCtx, user),
|
||||
),
|
||||
])),
|
||||
|
||||
@ -10,6 +10,7 @@ import 'package:manager_app/Components/message_notification.dart';
|
||||
import 'package:manager_app/Components/rounded_button.dart';
|
||||
import 'package:manager_app/Components/rounded_input_field.dart';
|
||||
import 'package:manager_app/Components/rounded_password_field.dart';
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:manager_app/Helpers/FileHelper.dart';
|
||||
import 'package:manager_app/Models/managerContext.dart';
|
||||
import 'package:manager_app/Models/session.dart';
|
||||
@ -88,7 +89,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
userRole = token.role;
|
||||
|
||||
showNotification(
|
||||
kSuccess, kWhite, 'Connexion réussie', context, null);
|
||||
kSuccess, kWhite, AppLocalizations.of(context)!.loginSuccess, context, null);
|
||||
|
||||
if (isRememberMe) {
|
||||
if (!localStorage.containsKey("remember")) {
|
||||
@ -171,7 +172,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
(Route<dynamic> route) => false // For pushAndRemoveUntil
|
||||
);*/
|
||||
} else {
|
||||
showNotification(Colors.orange, kWhite, 'Un problème est survenu lors de la connexion', context, null);
|
||||
showNotification(Colors.orange, kWhite, AppLocalizations.of(context)!.loginError, context, null);
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
@ -181,7 +182,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
print("error auth");
|
||||
print(e);
|
||||
if(fromClick) {
|
||||
showNotification(Colors.orange, kWhite, 'Un problème est survenu lors de la connexion', context, null);
|
||||
showNotification(Colors.orange, kWhite, AppLocalizations.of(context)!.loginError, context, null);
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
@ -393,13 +394,13 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
},
|
||||
),
|
||||
),
|
||||
Text("Se souvenir de moi", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),),
|
||||
Text(AppLocalizations.of(context)!.rememberMe, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: size.height * 0.015),
|
||||
!isLoading ? RoundedButton(
|
||||
text: "SE CONNECTER",
|
||||
text: AppLocalizations.of(context)!.connect,
|
||||
fontSize: 16,
|
||||
vertical: 15,
|
||||
horizontal: 30,
|
||||
|
||||
35
lib/Services/ai_translate_service.dart
Normal file
35
lib/Services/ai_translate_service.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class AiTranslateService {
|
||||
static Future<Map<String, String>> translate({
|
||||
required String host,
|
||||
required String accessToken,
|
||||
required String instanceId,
|
||||
required String text,
|
||||
required String sourceLang,
|
||||
required List<String> targetLangs,
|
||||
}) async {
|
||||
final uri = Uri.parse('$host/api/Ai/translate?instanceId=$instanceId');
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: jsonEncode({
|
||||
'text': text,
|
||||
'sourceLang': sourceLang,
|
||||
'targetLangs': targetLangs,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Erreur traduction IA : ${response.statusCode}');
|
||||
}
|
||||
|
||||
final data = jsonDecode(response.body);
|
||||
final translations = data['translations'] as Map<String, dynamic>;
|
||||
return translations.map((k, v) => MapEntry(k, v.toString()));
|
||||
}
|
||||
}
|
||||
@ -19,4 +19,9 @@ class AppContext with ChangeNotifier {
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setLocale(Locale locale) {
|
||||
_managerContext?.locale = locale;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +50,9 @@ class Client {
|
||||
NotificationApi? _notificationApi;
|
||||
NotificationApi? get notificationApi => _notificationApi;
|
||||
|
||||
SubscriptionPlanApi? _subscriptionPlanApi;
|
||||
SubscriptionPlanApi? get subscriptionPlanApi => _subscriptionPlanApi;
|
||||
|
||||
Client(String path) {
|
||||
_apiClient = ApiClient(basePath: path);
|
||||
//basePath: "https://192.168.31.140");
|
||||
@ -70,5 +73,6 @@ class Client {
|
||||
_statsApi = StatsApi(_apiClient);
|
||||
_apiKeyApi = ApiKeyApi(_apiClient);
|
||||
_notificationApi = NotificationApi(_apiClient);
|
||||
_subscriptionPlanApi = SubscriptionPlanApi(_apiClient);
|
||||
}
|
||||
}
|
||||
|
||||
461
lib/l10n/app_en.arb
Normal file
461
lib/l10n/app_en.arb
Normal file
@ -0,0 +1,461 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
|
||||
"cancel": "Cancel",
|
||||
"save": "Save",
|
||||
"create": "Create",
|
||||
"delete": "Delete",
|
||||
"close": "Close",
|
||||
"edit": "Edit",
|
||||
"actions": "Actions",
|
||||
"status": "Status",
|
||||
"no": "No",
|
||||
"name": "Name",
|
||||
"type": "Type",
|
||||
"date": "Date",
|
||||
|
||||
"loginSuccess": "Login successful",
|
||||
"loginError": "An error occurred during login",
|
||||
"rememberMe": "Remember me",
|
||||
"connect": "LOG IN",
|
||||
|
||||
"menuApplications": "Applications",
|
||||
"menuConfigurations": "Configurations",
|
||||
"menuResources": "Resources",
|
||||
"menuStatistics": "Statistics",
|
||||
"menuNotifications": "Notifications",
|
||||
"menuUsers": "Users",
|
||||
"menuApiKeys": "API Keys",
|
||||
|
||||
"noPlansAvailable": "No plans available. Create a plan first.",
|
||||
"noPlan": "No plan (unlimited)",
|
||||
"unlimitedStorage": "Unlimited storage",
|
||||
"unlimitedAI": "Unlimited AI",
|
||||
"aiRequestsPerMonth": "{count} AI req/month",
|
||||
"@aiRequestsPerMonth": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"storageLabel": "Storage",
|
||||
"aiThisMonthLabel": "AI this month",
|
||||
"unlimited": "Unlimited",
|
||||
"requestsAbbr": "req",
|
||||
"planUpdated": "Plan updated",
|
||||
"errorMessage": "Error: {error}",
|
||||
"@errorMessage": {
|
||||
"placeholders": {
|
||||
"error": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"switchInstance": "Switch instance",
|
||||
"noInstanceFound": "No instance found",
|
||||
"configurePlan": "Configure plan",
|
||||
"tooltipSwitchInstance": "Switch instance",
|
||||
|
||||
"usersTitle": "Users",
|
||||
"createUserBtn": "Create user",
|
||||
"createUserTitle": "Create user",
|
||||
"editUserTitle": "Edit user",
|
||||
"deleteUserTitle": "Delete user",
|
||||
"deleteUserConfirm": "Delete {email}?",
|
||||
"@deleteUserConfirm": {
|
||||
"placeholders": {
|
||||
"email": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"noUsers": "No users",
|
||||
"email": "Email",
|
||||
"firstName": "First name",
|
||||
"lastName": "Last name",
|
||||
"password": "Password",
|
||||
"role": "Role",
|
||||
"tooltipEdit": "Edit",
|
||||
"tooltipDelete": "Delete",
|
||||
|
||||
"notificationsTitle": "Notifications",
|
||||
"newMessage": "New message",
|
||||
"messageTitle": "Title",
|
||||
"messageBody": "Message",
|
||||
"schedule": "Schedule",
|
||||
"time": "Time",
|
||||
"send": "Send",
|
||||
"cancelNotification": "Cancel notification",
|
||||
"cancelNotificationConfirm": "Cancel « {title} »?",
|
||||
"@cancelNotificationConfirm": {
|
||||
"placeholders": {
|
||||
"title": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"allNotifications": "All",
|
||||
"sentNotifications": "Sent",
|
||||
"scheduledNotifications": "Scheduled",
|
||||
"failedNotifications": "Failed",
|
||||
"noNotifications": "No notifications sent",
|
||||
"noNotificationsForFilter": "No notifications for this filter",
|
||||
"topic": "Topic",
|
||||
"tooltipCancelNotification": "Cancel",
|
||||
"resultsCount": "{count, plural, one{{count} result} other{{count} results}}",
|
||||
"@resultsCount": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
|
||||
"apiKeysTitle": "API Keys",
|
||||
"createApiKey": "Create API key",
|
||||
"createKeyBtn": "Create key",
|
||||
"appType": "Application type",
|
||||
"noExpiration": "No expiration",
|
||||
"expiresOn": "Expires on {date}",
|
||||
"@expiresOn": {
|
||||
"placeholders": {
|
||||
"date": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"choose": "Choose",
|
||||
"removeExpiration": "Remove expiration",
|
||||
"apiKeyCreatedTitle": "API key created",
|
||||
"copyKeyWarning": "Copy this key now — it won't be shown again.",
|
||||
"copy": "Copy",
|
||||
"copiedKey": "I've copied the key",
|
||||
"revokeApiKeyTitle": "Revoke API key",
|
||||
"revokeApiKeyConfirm": "Revoke « {name} »? Apps using this key will lose access.",
|
||||
"@revokeApiKeyConfirm": {
|
||||
"placeholders": {
|
||||
"name": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"revoke": "Revoke",
|
||||
"noApiKeys": "No API keys",
|
||||
"createdOn": "Created on",
|
||||
"expiration": "Expiration",
|
||||
"activeKey": "Active",
|
||||
"revokedKey": "Revoked",
|
||||
"tooltipRevoke": "Revoke",
|
||||
|
||||
"statisticsTitle": "Statistics",
|
||||
"statsLoadError": "Unable to load statistics",
|
||||
"statsNoData": "No data yet for this period",
|
||||
"statsNoDataForType": "No events received for type \"{type}\"",
|
||||
"@statsNoDataForType": {
|
||||
"placeholders": {
|
||||
"type": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"statsSessions": "Sessions",
|
||||
"statsAvgDuration": "Avg. duration",
|
||||
"statsTopApp": "Top app",
|
||||
"statsTopLang": "Top language",
|
||||
"statsVisitsByDay": "Visits per day",
|
||||
"statsAll": "All",
|
||||
"statsTopSections": "Top sections",
|
||||
"statsApps": "Apps",
|
||||
"statsLanguages": "Languages",
|
||||
"statsTopPOI": "Top POI",
|
||||
"statsPOI": "POI",
|
||||
"statsTaps": "Taps",
|
||||
"statsTopAgenda": "Top agenda events",
|
||||
"statsEvent": "Event",
|
||||
"statsQuiz": "Quiz",
|
||||
"statsSection": "Section",
|
||||
"statsAvgScore": "Avg. score",
|
||||
"statsCompletions": "Completions",
|
||||
"statsGames": "Games",
|
||||
"statsGameType": "Type",
|
||||
"statsArticles": "Articles read",
|
||||
"statsReadings": "Reads",
|
||||
"statsMenuTitle": "Menu",
|
||||
"statsMenuItem": "Item",
|
||||
"statsQrScans": "QR Scans",
|
||||
"statsTotal": "Total",
|
||||
"statsValid": "Valid",
|
||||
"statsInvalid": "Invalid",
|
||||
"statsViews": "Views",
|
||||
|
||||
"noData": "No data",
|
||||
"errorOccurred": "An error occurred",
|
||||
"yes": "Yes",
|
||||
|
||||
"newConfiguration": "New configuration",
|
||||
"configNameLabel": "Name:",
|
||||
"orText": "or",
|
||||
"importLabel": "Import",
|
||||
"configNameRequired": "Please specify a name for the new visit",
|
||||
"configCreatedSuccess": "Configuration created successfully",
|
||||
"configExportSuccess": "Configuration exported successfully, file is at: {path}",
|
||||
"@configExportSuccess": {
|
||||
"placeholders": {
|
||||
"path": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"configExportFailed": "Configuration export failed",
|
||||
"configDeletedSuccess": "Configuration deleted successfully",
|
||||
"configSavedSuccess": "Configuration saved successfully",
|
||||
"configDeleteConfirm": "Are you sure you want to delete this configuration?",
|
||||
|
||||
"newSection": "New section",
|
||||
"newSubSection": "New sub section",
|
||||
"sectionNameLabel": "Name:",
|
||||
"sectionTypeLabel": "Type:",
|
||||
"sectionNameRequired": "Please specify a name for the new section",
|
||||
"sectionCreatedSuccess": "Section created successfully!",
|
||||
"sectionDeleteConfirm": "Are you sure you want to delete this section?",
|
||||
"sectionSavedSuccess": "Section saved successfully",
|
||||
"sectionTranslationSaved": "Section translations saved successfully",
|
||||
"sectionLoadError": "An error occurred while loading the section",
|
||||
"qrCodeCopied": "This QR code has been copied to the clipboard",
|
||||
"beaconLabel": "Beacon:",
|
||||
"beaconIdLabel": "Beacon ID:",
|
||||
"beaconMustBeNumber": "This must be a number",
|
||||
"identifierLabel": "Identifier:",
|
||||
"displayTitleLabel": "Display title:",
|
||||
"imageLabel": "Image:",
|
||||
"backgroundImageLabel": "Background image:",
|
||||
"questionInputLabel": "Question:",
|
||||
"videoUrlLabel": "Video URL:",
|
||||
"webUrlLabel": "Website URL:",
|
||||
|
||||
"subSectionUpdatedSuccess": "Sub section updated successfully",
|
||||
"subSectionUpdateError": "An error occurred while updating the sub section",
|
||||
"subSectionDeletedSuccess": "Sub section deleted successfully",
|
||||
"subSectionDeleteError": "An error occurred while deleting the sub section",
|
||||
"subSectionOrderUpdatedSuccess": "Sub section order updated successfully",
|
||||
"subSectionOrderUpdateError": "An error occurred while updating the sub section order",
|
||||
"subSectionCreatedSuccess": "Sub section created successfully",
|
||||
"subSectionCreateError": "An error occurred while creating the sub section",
|
||||
|
||||
"questionsLabel": "Questions",
|
||||
"questionLabel": "Question",
|
||||
"editQuestion": "Edit question",
|
||||
"questionDeleteConfirm": "Are you sure you want to delete this question?",
|
||||
"questionsLoadError": "An error occurred while loading the questions",
|
||||
"quizBadScore": "Bad score",
|
||||
"quizMediumScore": "Medium score",
|
||||
"quizGoodScore": "Good score",
|
||||
"quizExcellentScore": "Excellent score",
|
||||
"quizBadScoreMsg": "Message for a bad score",
|
||||
"quizMediumScoreMsg": "Message for a medium score",
|
||||
"quizGoodScoreMsg": "Message for a good score",
|
||||
"quizExcellentScoreMsg": "Message for an excellent score",
|
||||
"questionOrderUpdatedSuccess": "Question order updated successfully",
|
||||
"questionOrderUpdateError": "An error occurred while updating the question order",
|
||||
"questionCreatedSuccess": "Question created successfully",
|
||||
"questionCreateError": "An error occurred while creating the question",
|
||||
"questionUpdatedSuccess": "Question updated successfully",
|
||||
"questionUpdateError": "An error occurred while updating the question",
|
||||
"questionDeletedSuccess": "Question deleted successfully",
|
||||
"questionDeleteError": "An error occurred while deleting the question",
|
||||
"translationIncomplete": "Translation is incomplete",
|
||||
|
||||
"geopointCreatedSuccess": "Point created successfully",
|
||||
"geopointCreateError": "An error occurred while creating the point",
|
||||
"geopointUpdatedSuccess": "Point updated successfully",
|
||||
"geopointUpdateError": "An error occurred while updating the point",
|
||||
"geopointDeletedSuccess": "Point deleted successfully",
|
||||
"geopointDeleteError": "An error occurred while deleting the point",
|
||||
"categoryCreatedSuccess": "Category created successfully",
|
||||
"categoryCreateError": "An error occurred while creating the category",
|
||||
"categoryUpdatedSuccess": "Category updated successfully",
|
||||
"categoryUpdateError": "An error occurred while updating the category",
|
||||
|
||||
"eventCreatedSuccess": "Event created successfully",
|
||||
"eventCreateError": "An error occurred while creating the event",
|
||||
"eventUpdatedSuccess": "Event updated successfully",
|
||||
"eventUpdateError": "An error occurred while updating the event",
|
||||
"eventDeletedSuccess": "Event deleted successfully",
|
||||
"eventDeleteError": "An error occurred while deleting the event",
|
||||
"annotationCreatedSuccess": "Annotation created successfully",
|
||||
"annotationCreateError": "An error occurred while creating the annotation",
|
||||
"annotationUpdatedSuccess": "Annotation updated successfully",
|
||||
"annotationUpdateError": "An error occurred while updating the annotation",
|
||||
"programmeBlockCreatedSuccess": "Block created successfully",
|
||||
"programmeBlockCreateError": "An error occurred while creating the block",
|
||||
"programmeBlockUpdatedSuccess": "Block updated successfully",
|
||||
"programmeBlockUpdateError": "An error occurred while updating the block",
|
||||
|
||||
"pathCreatedSuccess": "Path created successfully",
|
||||
"pathCreateError": "An error occurred while creating the path",
|
||||
"pathUpdatedSuccess": "Path updated successfully",
|
||||
"pathUpdateError": "An error occurred while updating the path",
|
||||
"stepCreatedSuccess": "Step created successfully",
|
||||
"stepCreateError": "An error occurred while creating the step",
|
||||
"stepUpdatedSuccess": "Step updated successfully",
|
||||
"stepUpdateError": "An error occurred while updating the step",
|
||||
|
||||
"agendaEventsLabel": "Events",
|
||||
"agendaEventFallback": "Event",
|
||||
"addEvent": "Add an event",
|
||||
"noEvents": "No events",
|
||||
"noAddress": "No address",
|
||||
"onlineLabel": "Online:",
|
||||
"mapViewLabel": "Map view:",
|
||||
"mapServiceLabel": "Map service:",
|
||||
"jsonFilesLabel": "JSON files:",
|
||||
"jsonLabel": "JSON",
|
||||
|
||||
"guidedPathsLabel": "Guided Paths",
|
||||
"addPath": "Add a path",
|
||||
"noPathConfigured": "No path configured",
|
||||
"pathOrderUpdateError": "Error updating order",
|
||||
"pathNoTitle": "Untitled path",
|
||||
"pathDeletedSuccess": "Path deleted successfully",
|
||||
"pathDeleteError": "Error deleting path",
|
||||
"pathsLabel": "Paths",
|
||||
|
||||
"pointsOfInterestLabel": "Points of Interest",
|
||||
"geopointsLabel": "Geographic points",
|
||||
"searchLabel": "Search:",
|
||||
"geopointsLoadError": "Error loading geographic points",
|
||||
"geopointDeleteConfirm": "Are you sure you want to delete this geographic point?",
|
||||
"serviceLabel": "Service:",
|
||||
"centerPointLabel": "Center point:",
|
||||
"iconLabel": "Icon:",
|
||||
"listViewLabel": "List view:",
|
||||
"typeLabel": "Type:",
|
||||
"zoomLabel": "Zoom:",
|
||||
"categoriesLabel": "Categories:",
|
||||
|
||||
"startDateLabel": "Start date",
|
||||
"notDefined": "Not defined",
|
||||
"endDateLabel": "End date",
|
||||
"programmeLabel": "Programme",
|
||||
"addBlock": "Add a block",
|
||||
"blockFallback": "Block",
|
||||
"noBlocks": "No programme blocks defined",
|
||||
"programmeBlockDeletedSuccess": "Block deleted successfully",
|
||||
"programmeBlockDeleteError": "Error deleting block",
|
||||
"baseMapLabel": "Base map",
|
||||
"mapLabel": "Map:",
|
||||
"globalAnnotationsLabel": "Global annotations",
|
||||
"addAnnotation": "Add an annotation",
|
||||
"noAnnotations": "No global annotations",
|
||||
"annotationFallback": "Annotation",
|
||||
"annotationDeletedSuccess": "Annotation deleted",
|
||||
"annotationDeleteError": "Error deleting",
|
||||
|
||||
"agendaEventCreatedSuccess": "Event created successfully",
|
||||
"agendaEventCreateError": "An error occurred while creating the event",
|
||||
"agendaEventUpdatedSuccess": "Event updated successfully",
|
||||
"agendaEventUpdateError": "An error occurred while updating the event",
|
||||
"agendaEventDeletedSuccess": "Event deleted successfully",
|
||||
"agendaEventDeleteError": "An error occurred while deleting the event",
|
||||
|
||||
"newResource": "New resource",
|
||||
"resourceCreatedSuccess": "Resource created successfully",
|
||||
"resourceCreateError": "An error occurred while creating the resource",
|
||||
"resourceUpdatedSuccess": "Resource updated successfully",
|
||||
"resourceNoFileLoaded": "No file has been loaded",
|
||||
"resourceNameRequired": "Please give the resource a name",
|
||||
"resourceTooLarge": "Error: resource size must be under 1.5MB",
|
||||
"resourceInvalidUrl": "The URL is invalid",
|
||||
"resourceUrlRequired": "Please fill in the URL field",
|
||||
|
||||
"generalInfo": "General information",
|
||||
"mainImageLabel": "Main image:",
|
||||
"loaderLabel": "Loader:",
|
||||
"primaryColorLabel": "Primary color:",
|
||||
"secondaryColorLabel": "Secondary color:",
|
||||
"layoutLabel": "Layout:",
|
||||
"layoutGrid": "Grid",
|
||||
"languagesLabel": "Languages:",
|
||||
"featuredEventLabel": "Featured event:",
|
||||
"chooseEvent": "Choose an event",
|
||||
"noneOption": "None",
|
||||
"aiAssistantLabel": "AI assistant:",
|
||||
"appUpdatedSuccess": "Mobile application updated",
|
||||
"configActivatedSuccess": "Configuration activated successfully",
|
||||
"configDeactivatedSuccess": "Configuration deactivated successfully",
|
||||
"configRemoveConfirm": "Are you sure you want to remove this configuration from the application?",
|
||||
"configRemovedSuccess": "Configuration removed from application successfully",
|
||||
"configRemoveError": "An error occurred while removing the configuration",
|
||||
"phoneConfigTitle": "Configurations per device",
|
||||
"addConfig": "Add a configuration",
|
||||
"selectConfigToAdd": "Select a configuration to add",
|
||||
"noItems": "No items to display",
|
||||
"add": "Add",
|
||||
|
||||
"pinCode": "Pin code: {code}",
|
||||
"@pinCode": {
|
||||
"placeholders": {
|
||||
"code": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"tablets": "Tablets",
|
||||
|
||||
"stepsCount": "{count} steps",
|
||||
"@stepsCount": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"displayedDescriptionLabel": "Displayed description:",
|
||||
"displayedContentLabel": "Displayed content:",
|
||||
"displayedNameLabel": "Display name:",
|
||||
"startTimeLabel": "Start time",
|
||||
"noAnnotationConfigured": "No annotations configured.",
|
||||
"newStepTitle": "New step",
|
||||
"editStepTitle": "Edit step",
|
||||
"stepTitleLabel": "Step title",
|
||||
"stepDescriptionLabel": "Step description",
|
||||
"stepLocationLabel": "Step location:",
|
||||
"initiallyHiddenLabel": "Initially hidden",
|
||||
"lockedLabel": "Locked",
|
||||
"durationSecondsLabel": "Duration (seconds):",
|
||||
"triggerGeopointLabel": "Trigger GeoPoint (ID):",
|
||||
"questionsChallengesLabel": "Questions / Challenges",
|
||||
"noQuestionsConfigured": "No questions configured.",
|
||||
"newAgendaEventTitle": "New event",
|
||||
"editAgendaEventTitle": "Edit event",
|
||||
"eventTitleLabel": "Event title",
|
||||
"eventDescriptionLabel": "Event description",
|
||||
"startDateColonLabel": "Start date:",
|
||||
"videoResourceLabel": "Video:",
|
||||
"directVideoLinkLabel": "Direct video link:",
|
||||
"phoneLabel": "Phone:",
|
||||
"locationGeometryLabel": "Location / Geometry",
|
||||
"geometryLabel": "Geometry:",
|
||||
"materialIconLabel": "Icon (material):",
|
||||
"linearLabel": "Linear:",
|
||||
"requiredSuccessLabel": "Required success:",
|
||||
"pathStepsLabel": "Path steps",
|
||||
"noStepConfigured": "No point/step configured.",
|
||||
"stepFallback": "Step",
|
||||
"questionAskedLabel": "Question asked:",
|
||||
"questionTitleLabel": "Question title",
|
||||
"expectedAnswerLabel": "Expected answer:",
|
||||
"expectedAnswerModalLabel": "Expected answer",
|
||||
"validationNote": "Validation is done by comparison (case-insensitive).",
|
||||
"possibleAnswersLabel": "Possible answers:",
|
||||
"noAnswerDefined": "No answer defined. Add at least one.",
|
||||
"correctAnswer": "Correct answer ✓",
|
||||
"wrongAnswer": "Wrong answer",
|
||||
"answerNote": "✓ = correct answer. Multiple can be correct.",
|
||||
"geopointZoneTitle": "Geographic point / Zone",
|
||||
"geometryTypeLabel": "Geometry (Point/Line/Zone):",
|
||||
"phoneModalLabel": "Phone",
|
||||
"categoryLabel": "Category:",
|
||||
"startMessageLabel": "Start message:",
|
||||
"startMessageModalLabel": "Start message",
|
||||
"createCategoryTitle": "Create category",
|
||||
"editCategoryTitle": "Edit category",
|
||||
"categoryIconLabel": "Category icon:",
|
||||
"selectResource": "Select a resource",
|
||||
"createPdfTitle": "Create PDF",
|
||||
"answersLabel": "Answers",
|
||||
"answerLabel": "Answer",
|
||||
"createAnswerTitle": "Create answer",
|
||||
"editAnswerTitle": "Edit answer",
|
||||
"answerValidNote": "If checked, the answer is valid",
|
||||
"selectConfiguration": "Select a configuration",
|
||||
"noConfigFound": "No configuration found",
|
||||
"backgroundColorLabel": "Background color:",
|
||||
"updateTabletBtn": "Update tablet",
|
||||
"kioskUpdatedSuccess": "Kiosk updated",
|
||||
"webViewError": "The web page cannot be displayed, the URL is incorrect or empty",
|
||||
"download": "Download",
|
||||
"resourceDeleteConfirm": "Are you sure you want to delete this resource?",
|
||||
"categoriesTitle": "Categories",
|
||||
"endTimeLabel": "End time",
|
||||
"annotationsLabel": "Annotations"
|
||||
}
|
||||
461
lib/l10n/app_fr.arb
Normal file
461
lib/l10n/app_fr.arb
Normal file
@ -0,0 +1,461 @@
|
||||
{
|
||||
"@@locale": "fr",
|
||||
|
||||
"cancel": "Annuler",
|
||||
"save": "Enregistrer",
|
||||
"create": "Créer",
|
||||
"delete": "Supprimer",
|
||||
"close": "Fermer",
|
||||
"edit": "Modifier",
|
||||
"actions": "Actions",
|
||||
"status": "Statut",
|
||||
"no": "Non",
|
||||
"name": "Nom",
|
||||
"type": "Type",
|
||||
"date": "Date",
|
||||
|
||||
"loginSuccess": "Connexion réussie",
|
||||
"loginError": "Un problème est survenu lors de la connexion",
|
||||
"rememberMe": "Se souvenir de moi",
|
||||
"connect": "SE CONNECTER",
|
||||
|
||||
"menuApplications": "Applications",
|
||||
"menuConfigurations": "Configurations",
|
||||
"menuResources": "Ressources",
|
||||
"menuStatistics": "Statistiques",
|
||||
"menuNotifications": "Notifications",
|
||||
"menuUsers": "Utilisateurs",
|
||||
"menuApiKeys": "Clés API",
|
||||
|
||||
"noPlansAvailable": "Aucun plan disponible. Créez d'abord un plan.",
|
||||
"noPlan": "Aucun plan (illimité)",
|
||||
"unlimitedStorage": "Stockage illimité",
|
||||
"unlimitedAI": "IA illimitée",
|
||||
"aiRequestsPerMonth": "{count} req IA/mois",
|
||||
"@aiRequestsPerMonth": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"storageLabel": "Stockage",
|
||||
"aiThisMonthLabel": "IA ce mois",
|
||||
"unlimited": "Illimité",
|
||||
"requestsAbbr": "req",
|
||||
"planUpdated": "Plan mis à jour",
|
||||
"errorMessage": "Erreur : {error}",
|
||||
"@errorMessage": {
|
||||
"placeholders": {
|
||||
"error": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"switchInstance": "Changer d'instance",
|
||||
"noInstanceFound": "Aucune instance trouvée",
|
||||
"configurePlan": "Configurer le plan",
|
||||
"tooltipSwitchInstance": "Changer d'instance",
|
||||
|
||||
"usersTitle": "Utilisateurs",
|
||||
"createUserBtn": "Créer un utilisateur",
|
||||
"createUserTitle": "Créer un utilisateur",
|
||||
"editUserTitle": "Modifier l'utilisateur",
|
||||
"deleteUserTitle": "Supprimer l'utilisateur",
|
||||
"deleteUserConfirm": "Supprimer {email} ?",
|
||||
"@deleteUserConfirm": {
|
||||
"placeholders": {
|
||||
"email": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"noUsers": "Aucun utilisateur",
|
||||
"email": "Email",
|
||||
"firstName": "Prénom",
|
||||
"lastName": "Nom",
|
||||
"password": "Mot de passe",
|
||||
"role": "Rôle",
|
||||
"tooltipEdit": "Modifier",
|
||||
"tooltipDelete": "Supprimer",
|
||||
|
||||
"notificationsTitle": "Notifications",
|
||||
"newMessage": "Nouveau message",
|
||||
"messageTitle": "Titre",
|
||||
"messageBody": "Message",
|
||||
"schedule": "Planifier",
|
||||
"time": "Heure",
|
||||
"send": "Envoyer",
|
||||
"cancelNotification": "Annuler la notification",
|
||||
"cancelNotificationConfirm": "Annuler « {title} » ?",
|
||||
"@cancelNotificationConfirm": {
|
||||
"placeholders": {
|
||||
"title": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"allNotifications": "Toutes",
|
||||
"sentNotifications": "Envoyées",
|
||||
"scheduledNotifications": "Planifiées",
|
||||
"failedNotifications": "Échouées",
|
||||
"noNotifications": "Aucune notification envoyée",
|
||||
"noNotificationsForFilter": "Aucune notification pour ce filtre",
|
||||
"topic": "Topic",
|
||||
"tooltipCancelNotification": "Annuler",
|
||||
"resultsCount": "{count, plural, one{{count} résultat} other{{count} résultats}}",
|
||||
"@resultsCount": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
|
||||
"apiKeysTitle": "Clés API",
|
||||
"createApiKey": "Créer une clé API",
|
||||
"createKeyBtn": "Créer une clé",
|
||||
"appType": "Type d'application",
|
||||
"noExpiration": "Pas d'expiration",
|
||||
"expiresOn": "Expire le {date}",
|
||||
"@expiresOn": {
|
||||
"placeholders": {
|
||||
"date": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"choose": "Choisir",
|
||||
"removeExpiration": "Supprimer l'expiration",
|
||||
"apiKeyCreatedTitle": "Clé API créée",
|
||||
"copyKeyWarning": "Copiez cette clé maintenant — elle ne sera plus affichée.",
|
||||
"copy": "Copier",
|
||||
"copiedKey": "J'ai copié la clé",
|
||||
"revokeApiKeyTitle": "Révoquer la clé API",
|
||||
"revokeApiKeyConfirm": "Révoquer « {name} » ? Les apps utilisant cette clé perdront l'accès.",
|
||||
"@revokeApiKeyConfirm": {
|
||||
"placeholders": {
|
||||
"name": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"revoke": "Révoquer",
|
||||
"noApiKeys": "Aucune clé API",
|
||||
"createdOn": "Créée le",
|
||||
"expiration": "Expiration",
|
||||
"activeKey": "Active",
|
||||
"revokedKey": "Révoquée",
|
||||
"tooltipRevoke": "Révoquer",
|
||||
|
||||
"statisticsTitle": "Statistiques",
|
||||
"statsLoadError": "Impossible de charger les statistiques",
|
||||
"statsNoData": "Pas encore de données pour cette période",
|
||||
"statsNoDataForType": "Aucun event reçu pour le type \"{type}\"",
|
||||
"@statsNoDataForType": {
|
||||
"placeholders": {
|
||||
"type": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"statsSessions": "Sessions",
|
||||
"statsAvgDuration": "Durée moy.",
|
||||
"statsTopApp": "App top",
|
||||
"statsTopLang": "Langue top",
|
||||
"statsVisitsByDay": "Visites par jour",
|
||||
"statsAll": "Tous",
|
||||
"statsTopSections": "Top sections",
|
||||
"statsApps": "Apps",
|
||||
"statsLanguages": "Langues",
|
||||
"statsTopPOI": "Top POI",
|
||||
"statsPOI": "POI",
|
||||
"statsTaps": "Taps",
|
||||
"statsTopAgenda": "Top événements agenda",
|
||||
"statsEvent": "Événement",
|
||||
"statsQuiz": "Quiz",
|
||||
"statsSection": "Section",
|
||||
"statsAvgScore": "Score moy",
|
||||
"statsCompletions": "Complétions",
|
||||
"statsGames": "Jeux",
|
||||
"statsGameType": "Type",
|
||||
"statsArticles": "Articles lus",
|
||||
"statsReadings": "Lectures",
|
||||
"statsMenuTitle": "Menu",
|
||||
"statsMenuItem": "Item",
|
||||
"statsQrScans": "QR Scans",
|
||||
"statsTotal": "Total",
|
||||
"statsValid": "Valides",
|
||||
"statsInvalid": "Invalides",
|
||||
"statsViews": "Vues",
|
||||
|
||||
"noData": "Aucune donnée",
|
||||
"errorOccurred": "Une erreur est survenue",
|
||||
"yes": "Oui",
|
||||
|
||||
"newConfiguration": "Nouvelle configuration",
|
||||
"configNameLabel": "Nom :",
|
||||
"orText": "ou",
|
||||
"importLabel": "Importer",
|
||||
"configNameRequired": "Veuillez spécifier un nom pour la nouvelle visite",
|
||||
"configCreatedSuccess": "La configuration a été créée avec succès",
|
||||
"configExportSuccess": "L'export de la configuration a réussi, le document se trouve à cet endroit : {path}",
|
||||
"@configExportSuccess": {
|
||||
"placeholders": {
|
||||
"path": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"configExportFailed": "L'export de la configuration a échoué",
|
||||
"configDeletedSuccess": "La configuration a été supprimée avec succès",
|
||||
"configSavedSuccess": "La configuration a été sauvegardée avec succès",
|
||||
"configDeleteConfirm": "Êtes-vous sûr de vouloir supprimer cette configuration ?",
|
||||
|
||||
"newSection": "Nouvelle section",
|
||||
"newSubSection": "Nouvelle sous section",
|
||||
"sectionNameLabel": "Nom :",
|
||||
"sectionTypeLabel": "Type:",
|
||||
"sectionNameRequired": "Veuillez spécifier un nom pour la nouvelle section",
|
||||
"sectionCreatedSuccess": "La section a été créée avec succès !",
|
||||
"sectionDeleteConfirm": "Êtes-vous sûr de vouloir supprimer cette section ?",
|
||||
"sectionSavedSuccess": "La section a été sauvegardée avec succès",
|
||||
"sectionTranslationSaved": "Les traductions de la section ont été sauvegardées avec succès",
|
||||
"sectionLoadError": "Une erreur est survenue lors de la récupération de la section",
|
||||
"qrCodeCopied": "Ce QR code a été copié dans le presse papier",
|
||||
"beaconLabel": "Beacon :",
|
||||
"beaconIdLabel": "Identifiant Beacon :",
|
||||
"beaconMustBeNumber": "Cela doit être un chiffre",
|
||||
"identifierLabel": "Identifiant :",
|
||||
"displayTitleLabel": "Titre affiché:",
|
||||
"imageLabel": "Image :",
|
||||
"backgroundImageLabel": "Image fond d'écran :",
|
||||
"questionInputLabel": "Question :",
|
||||
"videoUrlLabel": "Url de la vidéo:",
|
||||
"webUrlLabel": "Url du site web:",
|
||||
|
||||
"subSectionUpdatedSuccess": "La sous section a été mis à jour avec succès",
|
||||
"subSectionUpdateError": "Une erreur est survenue lors de la mise à jour de la sous section",
|
||||
"subSectionDeletedSuccess": "La sous section a été supprimée avec succès",
|
||||
"subSectionDeleteError": "Une erreur est survenue lors de la suppression de la sous section",
|
||||
"subSectionOrderUpdatedSuccess": "L'ordre des sous sections a été mis à jour avec succès",
|
||||
"subSectionOrderUpdateError": "Une erreur est survenue lors de la mise à jour de l'ordre des sous sections",
|
||||
"subSectionCreatedSuccess": "La sous section a été créée avec succès",
|
||||
"subSectionCreateError": "Une erreur est survenue lors de la création de la sous section",
|
||||
|
||||
"questionsLabel": "Questions",
|
||||
"questionLabel": "Question",
|
||||
"editQuestion": "Modifier la question",
|
||||
"questionDeleteConfirm": "Êtes-vous sûr de vouloir supprimer cette question ?",
|
||||
"questionsLoadError": "Une erreur est survenue lors de la récupération des questions",
|
||||
"quizBadScore": "Mauvais score",
|
||||
"quizMediumScore": "Moyen score",
|
||||
"quizGoodScore": "Bon score",
|
||||
"quizExcellentScore": "Excellent score",
|
||||
"quizBadScoreMsg": "Message pour un mauvais score",
|
||||
"quizMediumScoreMsg": "Message pour un score moyen",
|
||||
"quizGoodScoreMsg": "Message pour un bon score",
|
||||
"quizExcellentScoreMsg": "Message pour un excellent score",
|
||||
"questionOrderUpdatedSuccess": "L'ordre des questions a été mis à jour avec succès",
|
||||
"questionOrderUpdateError": "Une erreur est survenue lors de la mise à jour de l'ordre des questions",
|
||||
"questionCreatedSuccess": "La question a été créée avec succès",
|
||||
"questionCreateError": "Une erreur est survenue lors de la création de la question",
|
||||
"questionUpdatedSuccess": "La question a été mis à jour avec succès",
|
||||
"questionUpdateError": "Une erreur est survenue lors de la mise à jour de la question",
|
||||
"questionDeletedSuccess": "La question a été supprimée avec succès",
|
||||
"questionDeleteError": "Une erreur est survenue lors de la suppression de la question",
|
||||
"translationIncomplete": "La traduction n'est pas complète",
|
||||
|
||||
"geopointCreatedSuccess": "Le point a été créé avec succès",
|
||||
"geopointCreateError": "Une erreur est survenue lors de la création du point",
|
||||
"geopointUpdatedSuccess": "Le point a été mis à jour avec succès",
|
||||
"geopointUpdateError": "Une erreur est survenue lors de la mise à jour du point",
|
||||
"geopointDeletedSuccess": "Le point a été supprimé avec succès",
|
||||
"geopointDeleteError": "Une erreur est survenue lors de la suppression du point",
|
||||
"categoryCreatedSuccess": "La catégorie a été créée avec succès",
|
||||
"categoryCreateError": "Une erreur est survenue lors de la création de la catégorie",
|
||||
"categoryUpdatedSuccess": "La catégorie a été mise à jour avec succès",
|
||||
"categoryUpdateError": "Une erreur est survenue lors de la mise à jour de la catégorie",
|
||||
|
||||
"eventCreatedSuccess": "L'événement a été créé avec succès",
|
||||
"eventCreateError": "Une erreur est survenue lors de la création de l'événement",
|
||||
"eventUpdatedSuccess": "L'événement a été mis à jour avec succès",
|
||||
"eventUpdateError": "Une erreur est survenue lors de la mise à jour de l'événement",
|
||||
"eventDeletedSuccess": "L'événement a été supprimé avec succès",
|
||||
"eventDeleteError": "Une erreur est survenue lors de la suppression de l'événement",
|
||||
"annotationCreatedSuccess": "L'annotation a été créée avec succès",
|
||||
"annotationCreateError": "Une erreur est survenue lors de la création de l'annotation",
|
||||
"annotationUpdatedSuccess": "L'annotation a été mise à jour avec succès",
|
||||
"annotationUpdateError": "Une erreur est survenue lors de la mise à jour de l'annotation",
|
||||
"programmeBlockCreatedSuccess": "Le bloc a été créé avec succès",
|
||||
"programmeBlockCreateError": "Une erreur est survenue lors de la création du bloc",
|
||||
"programmeBlockUpdatedSuccess": "Le bloc a été mis à jour avec succès",
|
||||
"programmeBlockUpdateError": "Une erreur est survenue lors de la mise à jour du bloc",
|
||||
|
||||
"pathCreatedSuccess": "Le parcours a été créé avec succès",
|
||||
"pathCreateError": "Une erreur est survenue lors de la création du parcours",
|
||||
"pathUpdatedSuccess": "Le parcours a été mis à jour avec succès",
|
||||
"pathUpdateError": "Une erreur est survenue lors de la mise à jour du parcours",
|
||||
"stepCreatedSuccess": "L'étape a été créée avec succès",
|
||||
"stepCreateError": "Une erreur est survenue lors de la création de l'étape",
|
||||
"stepUpdatedSuccess": "L'étape a été mise à jour avec succès",
|
||||
"stepUpdateError": "Une erreur est survenue lors de la mise à jour de l'étape",
|
||||
|
||||
"agendaEventsLabel": "Évènements",
|
||||
"agendaEventFallback": "Évènement",
|
||||
"addEvent": "Ajouter un évènement",
|
||||
"noEvents": "Aucun évènement",
|
||||
"noAddress": "Pas d'adresse",
|
||||
"onlineLabel": "En ligne :",
|
||||
"mapViewLabel": "Vue carte :",
|
||||
"mapServiceLabel": "Service carte :",
|
||||
"jsonFilesLabel": "Fichiers json :",
|
||||
"jsonLabel": "JSON",
|
||||
|
||||
"guidedPathsLabel": "Parcours Guidés",
|
||||
"addPath": "Ajouter un parcours",
|
||||
"noPathConfigured": "Aucun parcours configuré",
|
||||
"pathOrderUpdateError": "Erreur lors de la mise à jour de l'ordre",
|
||||
"pathNoTitle": "Parcours sans titre",
|
||||
"pathDeletedSuccess": "Parcours supprimé avec succès",
|
||||
"pathDeleteError": "Erreur lors de la suppression du parcours",
|
||||
"pathsLabel": "Parcours",
|
||||
|
||||
"pointsOfInterestLabel": "Points d'Intérêt",
|
||||
"geopointsLabel": "Points géographiques",
|
||||
"searchLabel": "Recherche :",
|
||||
"geopointsLoadError": "Une erreur est survenue lors de la récupération des points géographiques",
|
||||
"geopointDeleteConfirm": "Êtes-vous sûr de vouloir supprimer ce point géographique ?",
|
||||
"serviceLabel": "Service :",
|
||||
"centerPointLabel": "Point de centrage :",
|
||||
"iconLabel": "Icône :",
|
||||
"listViewLabel": "Vue liste :",
|
||||
"typeLabel": "Type :",
|
||||
"zoomLabel": "Zoom :",
|
||||
"categoriesLabel": "Catégories :",
|
||||
|
||||
"startDateLabel": "Date de début",
|
||||
"notDefined": "Non définie",
|
||||
"endDateLabel": "Date de fin",
|
||||
"programmeLabel": "Programme",
|
||||
"addBlock": "Ajouter un bloc",
|
||||
"blockFallback": "Bloc",
|
||||
"noBlocks": "Aucun bloc de programme défini",
|
||||
"programmeBlockDeletedSuccess": "Bloc supprimé avec succès",
|
||||
"programmeBlockDeleteError": "Erreur lors de la suppression du bloc",
|
||||
"baseMapLabel": "Carte de base",
|
||||
"mapLabel": "Carte :",
|
||||
"globalAnnotationsLabel": "Annotations globales",
|
||||
"addAnnotation": "Ajouter une annotation",
|
||||
"noAnnotations": "Aucune annotation globale",
|
||||
"annotationFallback": "Annotation",
|
||||
"annotationDeletedSuccess": "Annotation supprimée",
|
||||
"annotationDeleteError": "Erreur lors de la suppression",
|
||||
|
||||
"agendaEventCreatedSuccess": "L'événement a été créé avec succès",
|
||||
"agendaEventCreateError": "Une erreur est survenue lors de la création de l'événement",
|
||||
"agendaEventUpdatedSuccess": "L'événement a été mis à jour avec succès",
|
||||
"agendaEventUpdateError": "Une erreur est survenue lors de la mise à jour de l'événement",
|
||||
"agendaEventDeletedSuccess": "L'événement a été supprimé avec succès",
|
||||
"agendaEventDeleteError": "Une erreur est survenue lors de la suppression de l'événement",
|
||||
|
||||
"newResource": "Nouvelle ressource",
|
||||
"resourceCreatedSuccess": "La ressource a été créée avec succès",
|
||||
"resourceCreateError": "Une erreur est survenue lors de la création de la ressource",
|
||||
"resourceUpdatedSuccess": "La ressource a été mise à jour avec succès",
|
||||
"resourceNoFileLoaded": "Aucun fichier n'a été chargé",
|
||||
"resourceNameRequired": "Veuillez donner un nom à la ressource",
|
||||
"resourceTooLarge": "Erreur, attention, la taille de la ressource doit être inférieure à 1.5Mb",
|
||||
"resourceInvalidUrl": "L'url est invalide",
|
||||
"resourceUrlRequired": "Veuillez remplir le champ URL",
|
||||
|
||||
"generalInfo": "Informations générales",
|
||||
"mainImageLabel": "Image principale :",
|
||||
"loaderLabel": "Loader :",
|
||||
"primaryColorLabel": "Couleur principale :",
|
||||
"secondaryColorLabel": "Couleur secondaire :",
|
||||
"layoutLabel": "Affichage :",
|
||||
"layoutGrid": "Grille",
|
||||
"languagesLabel": "Langues :",
|
||||
"featuredEventLabel": "Evènement à l'affiche :",
|
||||
"chooseEvent": "Choisir un évènement",
|
||||
"noneOption": "Aucun",
|
||||
"aiAssistantLabel": "Assistant IA :",
|
||||
"appUpdatedSuccess": "Application mobile mise à jour",
|
||||
"configActivatedSuccess": "Configuration activée avec succès",
|
||||
"configDeactivatedSuccess": "Configuration désactivée avec succès",
|
||||
"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",
|
||||
"configRemoveError": "Une erreur est survenue lors du retrait de la configuration",
|
||||
"phoneConfigTitle": "Configurations par appareil",
|
||||
"addConfig": "Ajouter une configuration",
|
||||
"selectConfigToAdd": "Sélectionner une configuration à ajouter",
|
||||
"noItems": "Aucun élément à afficher",
|
||||
"add": "Ajouter",
|
||||
|
||||
"pinCode": "Code pin: {code}",
|
||||
"@pinCode": {
|
||||
"placeholders": {
|
||||
"code": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"tablets": "Tablettes",
|
||||
|
||||
"stepsCount": "{count} étapes",
|
||||
"@stepsCount": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"displayedDescriptionLabel": "Description affichée:",
|
||||
"displayedContentLabel": "Contenu affiché :",
|
||||
"displayedNameLabel": "Nom affiché :",
|
||||
"startTimeLabel": "Heure de début",
|
||||
"noAnnotationConfigured": "Aucune annotation configurée.",
|
||||
"newStepTitle": "Nouvelle Étape",
|
||||
"editStepTitle": "Modifier l'Étape",
|
||||
"stepTitleLabel": "Titre de l'étape",
|
||||
"stepDescriptionLabel": "Description de l'étape",
|
||||
"stepLocationLabel": "Emplacement de l'étape :",
|
||||
"initiallyHiddenLabel": "Cachée initialement",
|
||||
"lockedLabel": "Verrouillée",
|
||||
"durationSecondsLabel": "Durée (secondes) :",
|
||||
"triggerGeopointLabel": "GeoPoint de déclenchement (ID) :",
|
||||
"questionsChallengesLabel": "Questions / Défis",
|
||||
"noQuestionsConfigured": "Aucune question configurée.",
|
||||
"newAgendaEventTitle": "Nouvel Évènement",
|
||||
"editAgendaEventTitle": "Modifier l'Évènement",
|
||||
"eventTitleLabel": "Titre de l'évènement",
|
||||
"eventDescriptionLabel": "Description de l'évènement",
|
||||
"startDateColonLabel": "Date de début :",
|
||||
"videoResourceLabel": "Vidéo :",
|
||||
"directVideoLinkLabel": "Lien vidéo direct :",
|
||||
"phoneLabel": "Téléphone :",
|
||||
"locationGeometryLabel": "Localisation / Géométrie",
|
||||
"geometryLabel": "Géométrie :",
|
||||
"materialIconLabel": "Icône (material) :",
|
||||
"linearLabel": "Linéaire :",
|
||||
"requiredSuccessLabel": "Réussite requise :",
|
||||
"pathStepsLabel": "Étapes du parcours",
|
||||
"noStepConfigured": "Aucun point/étape configuré.",
|
||||
"stepFallback": "Étape",
|
||||
"questionAskedLabel": "Question posée :",
|
||||
"questionTitleLabel": "Intitulé de la question",
|
||||
"expectedAnswerLabel": "Réponse attendue :",
|
||||
"expectedAnswerModalLabel": "Réponse attendue",
|
||||
"validationNote": "La validation se fait par comparaison (insensible à la casse).",
|
||||
"possibleAnswersLabel": "Réponses possibles :",
|
||||
"noAnswerDefined": "Aucune réponse définie. Ajoutez-en au moins une.",
|
||||
"correctAnswer": "Bonne réponse ✓",
|
||||
"wrongAnswer": "Mauvaise réponse",
|
||||
"answerNote": "✓ = bonne réponse. Plusieurs peuvent être correctes.",
|
||||
"geopointZoneTitle": "Point géographique / Zone",
|
||||
"geometryTypeLabel": "Géométrie (Point/Ligne/Zone) :",
|
||||
"phoneModalLabel": "Téléphone",
|
||||
"categoryLabel": "Catégorie :",
|
||||
"startMessageLabel": "Message départ :",
|
||||
"startMessageModalLabel": "Message départ",
|
||||
"createCategoryTitle": "Création catégorie",
|
||||
"editCategoryTitle": "Modification catégorie",
|
||||
"categoryIconLabel": "Icône catégorie :",
|
||||
"selectResource": "Sélectionner une ressource",
|
||||
"createPdfTitle": "Création PDF",
|
||||
"answersLabel": "Réponses",
|
||||
"answerLabel": "Réponse",
|
||||
"createAnswerTitle": "Créer la réponse",
|
||||
"editAnswerTitle": "Modifier la réponse",
|
||||
"answerValidNote": "Si coché, la réponse est valide",
|
||||
"selectConfiguration": "Sélectionner une configuration",
|
||||
"noConfigFound": "Aucune configuration trouvée",
|
||||
"backgroundColorLabel": "Couleur fond d'écran :",
|
||||
"updateTabletBtn": "Mettre à jour la tablette",
|
||||
"kioskUpdatedSuccess": "Le kiosk a été mis à jour",
|
||||
"webViewError": "La page internet ne peut pas être affichée, l'url est incorrecte ou vide",
|
||||
"download": "Télécharger",
|
||||
"resourceDeleteConfirm": "Êtes-vous sûr de vouloir supprimer cette ressource ?",
|
||||
"categoriesTitle": "Catégories",
|
||||
"endTimeLabel": "Heure de fin",
|
||||
"annotationsLabel": "Annotations"
|
||||
}
|
||||
2405
lib/l10n/app_localizations.dart
Normal file
2405
lib/l10n/app_localizations.dart
Normal file
File diff suppressed because it is too large
Load Diff
1208
lib/l10n/app_localizations_en.dart
Normal file
1208
lib/l10n/app_localizations_en.dart
Normal file
File diff suppressed because it is too large
Load Diff
1239
lib/l10n/app_localizations_fr.dart
Normal file
1239
lib/l10n/app_localizations_fr.dart
Normal file
File diff suppressed because it is too large
Load Diff
1220
lib/l10n/app_localizations_nl.dart
Normal file
1220
lib/l10n/app_localizations_nl.dart
Normal file
File diff suppressed because it is too large
Load Diff
461
lib/l10n/app_nl.arb
Normal file
461
lib/l10n/app_nl.arb
Normal file
@ -0,0 +1,461 @@
|
||||
{
|
||||
"@@locale": "nl",
|
||||
|
||||
"cancel": "Annuleren",
|
||||
"save": "Opslaan",
|
||||
"create": "Aanmaken",
|
||||
"delete": "Verwijderen",
|
||||
"close": "Sluiten",
|
||||
"edit": "Bewerken",
|
||||
"actions": "Acties",
|
||||
"status": "Status",
|
||||
"no": "Nee",
|
||||
"name": "Naam",
|
||||
"type": "Type",
|
||||
"date": "Datum",
|
||||
|
||||
"loginSuccess": "Inloggen geslaagd",
|
||||
"loginError": "Er is een probleem opgetreden bij het inloggen",
|
||||
"rememberMe": "Onthoud mij",
|
||||
"connect": "INLOGGEN",
|
||||
|
||||
"menuApplications": "Applicaties",
|
||||
"menuConfigurations": "Configuraties",
|
||||
"menuResources": "Bronnen",
|
||||
"menuStatistics": "Statistieken",
|
||||
"menuNotifications": "Meldingen",
|
||||
"menuUsers": "Gebruikers",
|
||||
"menuApiKeys": "API-sleutels",
|
||||
|
||||
"noPlansAvailable": "Geen plannen beschikbaar. Maak eerst een plan aan.",
|
||||
"noPlan": "Geen plan (onbeperkt)",
|
||||
"unlimitedStorage": "Onbeperkte opslag",
|
||||
"unlimitedAI": "Onbeperkte AI",
|
||||
"aiRequestsPerMonth": "{count} AI-verz./maand",
|
||||
"@aiRequestsPerMonth": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"storageLabel": "Opslag",
|
||||
"aiThisMonthLabel": "AI deze maand",
|
||||
"unlimited": "Onbeperkt",
|
||||
"requestsAbbr": "verz.",
|
||||
"planUpdated": "Plan bijgewerkt",
|
||||
"errorMessage": "Fout: {error}",
|
||||
"@errorMessage": {
|
||||
"placeholders": {
|
||||
"error": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"switchInstance": "Instantie wisselen",
|
||||
"noInstanceFound": "Geen instantie gevonden",
|
||||
"configurePlan": "Plan configureren",
|
||||
"tooltipSwitchInstance": "Instantie wisselen",
|
||||
|
||||
"usersTitle": "Gebruikers",
|
||||
"createUserBtn": "Gebruiker aanmaken",
|
||||
"createUserTitle": "Gebruiker aanmaken",
|
||||
"editUserTitle": "Gebruiker bewerken",
|
||||
"deleteUserTitle": "Gebruiker verwijderen",
|
||||
"deleteUserConfirm": "{email} verwijderen?",
|
||||
"@deleteUserConfirm": {
|
||||
"placeholders": {
|
||||
"email": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"noUsers": "Geen gebruikers",
|
||||
"email": "E-mail",
|
||||
"firstName": "Voornaam",
|
||||
"lastName": "Naam",
|
||||
"password": "Wachtwoord",
|
||||
"role": "Rol",
|
||||
"tooltipEdit": "Bewerken",
|
||||
"tooltipDelete": "Verwijderen",
|
||||
|
||||
"notificationsTitle": "Meldingen",
|
||||
"newMessage": "Nieuw bericht",
|
||||
"messageTitle": "Titel",
|
||||
"messageBody": "Bericht",
|
||||
"schedule": "Plannen",
|
||||
"time": "Tijd",
|
||||
"send": "Versturen",
|
||||
"cancelNotification": "Melding annuleren",
|
||||
"cancelNotificationConfirm": "« {title} » annuleren?",
|
||||
"@cancelNotificationConfirm": {
|
||||
"placeholders": {
|
||||
"title": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"allNotifications": "Alle",
|
||||
"sentNotifications": "Verzonden",
|
||||
"scheduledNotifications": "Gepland",
|
||||
"failedNotifications": "Mislukt",
|
||||
"noNotifications": "Geen meldingen verzonden",
|
||||
"noNotificationsForFilter": "Geen meldingen voor dit filter",
|
||||
"topic": "Onderwerp",
|
||||
"tooltipCancelNotification": "Annuleren",
|
||||
"resultsCount": "{count, plural, one{{count} resultaat} other{{count} resultaten}}",
|
||||
"@resultsCount": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
|
||||
"apiKeysTitle": "API-sleutels",
|
||||
"createApiKey": "API-sleutel aanmaken",
|
||||
"createKeyBtn": "Sleutel aanmaken",
|
||||
"appType": "Applicatietype",
|
||||
"noExpiration": "Geen vervaldatum",
|
||||
"expiresOn": "Verloopt op {date}",
|
||||
"@expiresOn": {
|
||||
"placeholders": {
|
||||
"date": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"choose": "Kiezen",
|
||||
"removeExpiration": "Vervaldatum verwijderen",
|
||||
"apiKeyCreatedTitle": "API-sleutel aangemaakt",
|
||||
"copyKeyWarning": "Kopieer deze sleutel nu — hij wordt niet meer weergegeven.",
|
||||
"copy": "Kopiëren",
|
||||
"copiedKey": "Ik heb de sleutel gekopieerd",
|
||||
"revokeApiKeyTitle": "API-sleutel intrekken",
|
||||
"revokeApiKeyConfirm": "« {name} » intrekken? Apps die deze sleutel gebruiken verliezen toegang.",
|
||||
"@revokeApiKeyConfirm": {
|
||||
"placeholders": {
|
||||
"name": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"revoke": "Intrekken",
|
||||
"noApiKeys": "Geen API-sleutels",
|
||||
"createdOn": "Aangemaakt op",
|
||||
"expiration": "Vervaldatum",
|
||||
"activeKey": "Actief",
|
||||
"revokedKey": "Ingetrokken",
|
||||
"tooltipRevoke": "Intrekken",
|
||||
|
||||
"statisticsTitle": "Statistieken",
|
||||
"statsLoadError": "Statistieken kunnen niet worden geladen",
|
||||
"statsNoData": "Nog geen gegevens voor deze periode",
|
||||
"statsNoDataForType": "Geen events ontvangen voor type \"{type}\"",
|
||||
"@statsNoDataForType": {
|
||||
"placeholders": {
|
||||
"type": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"statsSessions": "Sessies",
|
||||
"statsAvgDuration": "Gem. duur",
|
||||
"statsTopApp": "Top app",
|
||||
"statsTopLang": "Top taal",
|
||||
"statsVisitsByDay": "Bezoeken per dag",
|
||||
"statsAll": "Alle",
|
||||
"statsTopSections": "Top secties",
|
||||
"statsApps": "Apps",
|
||||
"statsLanguages": "Talen",
|
||||
"statsTopPOI": "Top POI",
|
||||
"statsPOI": "POI",
|
||||
"statsTaps": "Taps",
|
||||
"statsTopAgenda": "Top agenda-evenementen",
|
||||
"statsEvent": "Evenement",
|
||||
"statsQuiz": "Quiz",
|
||||
"statsSection": "Sectie",
|
||||
"statsAvgScore": "Gem. score",
|
||||
"statsCompletions": "Voltooiingen",
|
||||
"statsGames": "Spellen",
|
||||
"statsGameType": "Type",
|
||||
"statsArticles": "Gelezen artikelen",
|
||||
"statsReadings": "Lezingen",
|
||||
"statsMenuTitle": "Menu",
|
||||
"statsMenuItem": "Item",
|
||||
"statsQrScans": "QR-scans",
|
||||
"statsTotal": "Totaal",
|
||||
"statsValid": "Geldig",
|
||||
"statsInvalid": "Ongeldig",
|
||||
"statsViews": "Weergaven",
|
||||
|
||||
"noData": "Geen gegevens",
|
||||
"errorOccurred": "Er is een fout opgetreden",
|
||||
"yes": "Ja",
|
||||
|
||||
"newConfiguration": "Nieuwe configuratie",
|
||||
"configNameLabel": "Naam:",
|
||||
"orText": "of",
|
||||
"importLabel": "Importeren",
|
||||
"configNameRequired": "Geef een naam op voor het nieuwe bezoek",
|
||||
"configCreatedSuccess": "Configuratie succesvol aangemaakt",
|
||||
"configExportSuccess": "Configuratie succesvol geëxporteerd, bestand op: {path}",
|
||||
"@configExportSuccess": {
|
||||
"placeholders": {
|
||||
"path": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"configExportFailed": "Export van configuratie mislukt",
|
||||
"configDeletedSuccess": "Configuratie succesvol verwijderd",
|
||||
"configSavedSuccess": "Configuratie succesvol opgeslagen",
|
||||
"configDeleteConfirm": "Weet u zeker dat u deze configuratie wilt verwijderen?",
|
||||
|
||||
"newSection": "Nieuwe sectie",
|
||||
"newSubSection": "Nieuwe subsectie",
|
||||
"sectionNameLabel": "Naam:",
|
||||
"sectionTypeLabel": "Type:",
|
||||
"sectionNameRequired": "Geef een naam op voor de nieuwe sectie",
|
||||
"sectionCreatedSuccess": "Sectie succesvol aangemaakt!",
|
||||
"sectionDeleteConfirm": "Weet u zeker dat u deze sectie wilt verwijderen?",
|
||||
"sectionSavedSuccess": "Sectie succesvol opgeslagen",
|
||||
"sectionTranslationSaved": "Sectievertaling succesvol opgeslagen",
|
||||
"sectionLoadError": "Er is een fout opgetreden bij het laden van de sectie",
|
||||
"qrCodeCopied": "Deze QR-code is naar het klembord gekopieerd",
|
||||
"beaconLabel": "Beacon:",
|
||||
"beaconIdLabel": "Beacon-ID:",
|
||||
"beaconMustBeNumber": "Dit moet een getal zijn",
|
||||
"identifierLabel": "Identificator:",
|
||||
"displayTitleLabel": "Weergegeven titel:",
|
||||
"imageLabel": "Afbeelding:",
|
||||
"backgroundImageLabel": "Achtergrondafbeelding:",
|
||||
"questionInputLabel": "Vraag:",
|
||||
"videoUrlLabel": "Video-URL:",
|
||||
"webUrlLabel": "Website-URL:",
|
||||
|
||||
"subSectionUpdatedSuccess": "Subsectie succesvol bijgewerkt",
|
||||
"subSectionUpdateError": "Er is een fout opgetreden bij het bijwerken van de subsectie",
|
||||
"subSectionDeletedSuccess": "Subsectie succesvol verwijderd",
|
||||
"subSectionDeleteError": "Er is een fout opgetreden bij het verwijderen van de subsectie",
|
||||
"subSectionOrderUpdatedSuccess": "Volgorde van subsecties succesvol bijgewerkt",
|
||||
"subSectionOrderUpdateError": "Er is een fout opgetreden bij het bijwerken van de volgorde",
|
||||
"subSectionCreatedSuccess": "Subsectie succesvol aangemaakt",
|
||||
"subSectionCreateError": "Er is een fout opgetreden bij het aanmaken van de subsectie",
|
||||
|
||||
"questionsLabel": "Vragen",
|
||||
"questionLabel": "Vraag",
|
||||
"editQuestion": "Vraag bewerken",
|
||||
"questionDeleteConfirm": "Weet u zeker dat u deze vraag wilt verwijderen?",
|
||||
"questionsLoadError": "Er is een fout opgetreden bij het laden van de vragen",
|
||||
"quizBadScore": "Slechte score",
|
||||
"quizMediumScore": "Gemiddelde score",
|
||||
"quizGoodScore": "Goede score",
|
||||
"quizExcellentScore": "Uitstekende score",
|
||||
"quizBadScoreMsg": "Bericht voor een slechte score",
|
||||
"quizMediumScoreMsg": "Bericht voor een gemiddelde score",
|
||||
"quizGoodScoreMsg": "Bericht voor een goede score",
|
||||
"quizExcellentScoreMsg": "Bericht voor een uitstekende score",
|
||||
"questionOrderUpdatedSuccess": "Vraagvolgorde succesvol bijgewerkt",
|
||||
"questionOrderUpdateError": "Er is een fout opgetreden bij het bijwerken van de vraagvolgorde",
|
||||
"questionCreatedSuccess": "Vraag succesvol aangemaakt",
|
||||
"questionCreateError": "Er is een fout opgetreden bij het aanmaken van de vraag",
|
||||
"questionUpdatedSuccess": "Vraag succesvol bijgewerkt",
|
||||
"questionUpdateError": "Er is een fout opgetreden bij het bijwerken van de vraag",
|
||||
"questionDeletedSuccess": "Vraag succesvol verwijderd",
|
||||
"questionDeleteError": "Er is een fout opgetreden bij het verwijderen van de vraag",
|
||||
"translationIncomplete": "De vertaling is niet volledig",
|
||||
|
||||
"geopointCreatedSuccess": "Punt succesvol aangemaakt",
|
||||
"geopointCreateError": "Er is een fout opgetreden bij het aanmaken van het punt",
|
||||
"geopointUpdatedSuccess": "Punt succesvol bijgewerkt",
|
||||
"geopointUpdateError": "Er is een fout opgetreden bij het bijwerken van het punt",
|
||||
"geopointDeletedSuccess": "Punt succesvol verwijderd",
|
||||
"geopointDeleteError": "Er is een fout opgetreden bij het verwijderen van het punt",
|
||||
"categoryCreatedSuccess": "Categorie succesvol aangemaakt",
|
||||
"categoryCreateError": "Er is een fout opgetreden bij het aanmaken van de categorie",
|
||||
"categoryUpdatedSuccess": "Categorie succesvol bijgewerkt",
|
||||
"categoryUpdateError": "Er is een fout opgetreden bij het bijwerken van de categorie",
|
||||
|
||||
"eventCreatedSuccess": "Evenement succesvol aangemaakt",
|
||||
"eventCreateError": "Er is een fout opgetreden bij het aanmaken van het evenement",
|
||||
"eventUpdatedSuccess": "Evenement succesvol bijgewerkt",
|
||||
"eventUpdateError": "Er is een fout opgetreden bij het bijwerken van het evenement",
|
||||
"eventDeletedSuccess": "Evenement succesvol verwijderd",
|
||||
"eventDeleteError": "Er is een fout opgetreden bij het verwijderen van het evenement",
|
||||
"annotationCreatedSuccess": "Annotatie succesvol aangemaakt",
|
||||
"annotationCreateError": "Er is een fout opgetreden bij het aanmaken van de annotatie",
|
||||
"annotationUpdatedSuccess": "Annotatie succesvol bijgewerkt",
|
||||
"annotationUpdateError": "Er is een fout opgetreden bij het bijwerken van de annotatie",
|
||||
"programmeBlockCreatedSuccess": "Blok succesvol aangemaakt",
|
||||
"programmeBlockCreateError": "Er is een fout opgetreden bij het aanmaken van het blok",
|
||||
"programmeBlockUpdatedSuccess": "Blok succesvol bijgewerkt",
|
||||
"programmeBlockUpdateError": "Er is een fout opgetreden bij het bijwerken van het blok",
|
||||
|
||||
"pathCreatedSuccess": "Parcours succesvol aangemaakt",
|
||||
"pathCreateError": "Er is een fout opgetreden bij het aanmaken van het parcours",
|
||||
"pathUpdatedSuccess": "Parcours succesvol bijgewerkt",
|
||||
"pathUpdateError": "Er is een fout opgetreden bij het bijwerken van het parcours",
|
||||
"stepCreatedSuccess": "Stap succesvol aangemaakt",
|
||||
"stepCreateError": "Er is een fout opgetreden bij het aanmaken van de stap",
|
||||
"stepUpdatedSuccess": "Stap succesvol bijgewerkt",
|
||||
"stepUpdateError": "Er is een fout opgetreden bij het bijwerken van de stap",
|
||||
|
||||
"agendaEventsLabel": "Evenementen",
|
||||
"agendaEventFallback": "Evenement",
|
||||
"addEvent": "Evenement toevoegen",
|
||||
"noEvents": "Geen evenementen",
|
||||
"noAddress": "Geen adres",
|
||||
"onlineLabel": "Online:",
|
||||
"mapViewLabel": "Kaartweergave:",
|
||||
"mapServiceLabel": "Kaartservice:",
|
||||
"jsonFilesLabel": "JSON-bestanden:",
|
||||
"jsonLabel": "JSON",
|
||||
|
||||
"guidedPathsLabel": "Begeleide parcours",
|
||||
"addPath": "Parcours toevoegen",
|
||||
"noPathConfigured": "Geen parcours geconfigureerd",
|
||||
"pathOrderUpdateError": "Fout bij het bijwerken van de volgorde",
|
||||
"pathNoTitle": "Parcours zonder titel",
|
||||
"pathDeletedSuccess": "Parcours succesvol verwijderd",
|
||||
"pathDeleteError": "Fout bij het verwijderen van het parcours",
|
||||
"pathsLabel": "Parcours",
|
||||
|
||||
"pointsOfInterestLabel": "Bezienswaardigheden",
|
||||
"geopointsLabel": "Geografische punten",
|
||||
"searchLabel": "Zoeken:",
|
||||
"geopointsLoadError": "Fout bij het laden van geografische punten",
|
||||
"geopointDeleteConfirm": "Weet u zeker dat u dit geografische punt wilt verwijderen?",
|
||||
"serviceLabel": "Service:",
|
||||
"centerPointLabel": "Middelpunt:",
|
||||
"iconLabel": "Pictogram:",
|
||||
"listViewLabel": "Lijstweergave:",
|
||||
"typeLabel": "Type:",
|
||||
"zoomLabel": "Zoom:",
|
||||
"categoriesLabel": "Categorieën:",
|
||||
|
||||
"startDateLabel": "Startdatum",
|
||||
"notDefined": "Niet gedefinieerd",
|
||||
"endDateLabel": "Einddatum",
|
||||
"programmeLabel": "Programma",
|
||||
"addBlock": "Blok toevoegen",
|
||||
"blockFallback": "Blok",
|
||||
"noBlocks": "Geen programmablokken gedefinieerd",
|
||||
"programmeBlockDeletedSuccess": "Blok succesvol verwijderd",
|
||||
"programmeBlockDeleteError": "Fout bij het verwijderen van het blok",
|
||||
"baseMapLabel": "Basiskaart",
|
||||
"mapLabel": "Kaart:",
|
||||
"globalAnnotationsLabel": "Globale annotaties",
|
||||
"addAnnotation": "Annotatie toevoegen",
|
||||
"noAnnotations": "Geen globale annotaties",
|
||||
"annotationFallback": "Annotatie",
|
||||
"annotationDeletedSuccess": "Annotatie verwijderd",
|
||||
"annotationDeleteError": "Fout bij verwijderen",
|
||||
|
||||
"agendaEventCreatedSuccess": "Evenement succesvol aangemaakt",
|
||||
"agendaEventCreateError": "Er is een fout opgetreden bij het aanmaken van het evenement",
|
||||
"agendaEventUpdatedSuccess": "Evenement succesvol bijgewerkt",
|
||||
"agendaEventUpdateError": "Er is een fout opgetreden bij het bijwerken van het evenement",
|
||||
"agendaEventDeletedSuccess": "Evenement succesvol verwijderd",
|
||||
"agendaEventDeleteError": "Er is een fout opgetreden bij het verwijderen van het evenement",
|
||||
|
||||
"newResource": "Nieuwe bron",
|
||||
"resourceCreatedSuccess": "Bron succesvol aangemaakt",
|
||||
"resourceCreateError": "Er is een fout opgetreden bij het aanmaken van de bron",
|
||||
"resourceUpdatedSuccess": "Bron succesvol bijgewerkt",
|
||||
"resourceNoFileLoaded": "Er is geen bestand geladen",
|
||||
"resourceNameRequired": "Geef de bron een naam",
|
||||
"resourceTooLarge": "Fout: de bestandsgrootte moet kleiner zijn dan 1,5 MB",
|
||||
"resourceInvalidUrl": "De URL is ongeldig",
|
||||
"resourceUrlRequired": "Vul het URL-veld in",
|
||||
|
||||
"generalInfo": "Algemene informatie",
|
||||
"mainImageLabel": "Hoofdafbeelding:",
|
||||
"loaderLabel": "Loader:",
|
||||
"primaryColorLabel": "Primaire kleur:",
|
||||
"secondaryColorLabel": "Secundaire kleur:",
|
||||
"layoutLabel": "Weergave:",
|
||||
"layoutGrid": "Raster",
|
||||
"languagesLabel": "Talen:",
|
||||
"featuredEventLabel": "Uitgelicht evenement:",
|
||||
"chooseEvent": "Kies een evenement",
|
||||
"noneOption": "Geen",
|
||||
"aiAssistantLabel": "AI-assistent:",
|
||||
"appUpdatedSuccess": "Mobiele applicatie bijgewerkt",
|
||||
"configActivatedSuccess": "Configuratie succesvol geactiveerd",
|
||||
"configDeactivatedSuccess": "Configuratie succesvol gedeactiveerd",
|
||||
"configRemoveConfirm": "Weet u zeker dat u deze configuratie uit de applicatie wilt verwijderen?",
|
||||
"configRemovedSuccess": "Configuratie succesvol verwijderd uit de applicatie",
|
||||
"configRemoveError": "Er is een fout opgetreden bij het verwijderen van de configuratie",
|
||||
"phoneConfigTitle": "Configuraties per apparaat",
|
||||
"addConfig": "Configuratie toevoegen",
|
||||
"selectConfigToAdd": "Selecteer een configuratie om toe te voegen",
|
||||
"noItems": "Geen items om weer te geven",
|
||||
"add": "Toevoegen",
|
||||
|
||||
"pinCode": "Pincode: {code}",
|
||||
"@pinCode": {
|
||||
"placeholders": {
|
||||
"code": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"tablets": "Tablets",
|
||||
|
||||
"stepsCount": "{count} stappen",
|
||||
"@stepsCount": {
|
||||
"placeholders": {
|
||||
"count": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"displayedDescriptionLabel": "Weergegeven beschrijving:",
|
||||
"displayedContentLabel": "Weergegeven inhoud:",
|
||||
"displayedNameLabel": "Weergavenaam:",
|
||||
"startTimeLabel": "Begintijd",
|
||||
"noAnnotationConfigured": "Geen annotaties geconfigureerd.",
|
||||
"newStepTitle": "Nieuwe stap",
|
||||
"editStepTitle": "Stap bewerken",
|
||||
"stepTitleLabel": "Staptitel",
|
||||
"stepDescriptionLabel": "Stapbeschrijving",
|
||||
"stepLocationLabel": "Stap locatie:",
|
||||
"initiallyHiddenLabel": "Aanvankelijk verborgen",
|
||||
"lockedLabel": "Vergrendeld",
|
||||
"durationSecondsLabel": "Duur (seconden):",
|
||||
"triggerGeopointLabel": "GeoPoint trigger (ID):",
|
||||
"questionsChallengesLabel": "Vragen / Uitdagingen",
|
||||
"noQuestionsConfigured": "Geen vragen geconfigureerd.",
|
||||
"newAgendaEventTitle": "Nieuw evenement",
|
||||
"editAgendaEventTitle": "Evenement bewerken",
|
||||
"eventTitleLabel": "Evenementtitel",
|
||||
"eventDescriptionLabel": "Evenementbeschrijving",
|
||||
"startDateColonLabel": "Begindatum:",
|
||||
"videoResourceLabel": "Video:",
|
||||
"directVideoLinkLabel": "Directe videolink:",
|
||||
"phoneLabel": "Telefoon:",
|
||||
"locationGeometryLabel": "Locatie / Geometrie",
|
||||
"geometryLabel": "Geometrie:",
|
||||
"materialIconLabel": "Icoon (material):",
|
||||
"linearLabel": "Lineair:",
|
||||
"requiredSuccessLabel": "Vereist succes:",
|
||||
"pathStepsLabel": "Parcoursstappen",
|
||||
"noStepConfigured": "Geen punt/stap geconfigureerd.",
|
||||
"stepFallback": "Stap",
|
||||
"questionAskedLabel": "Gestelde vraag:",
|
||||
"questionTitleLabel": "Vraagstitel",
|
||||
"expectedAnswerLabel": "Verwacht antwoord:",
|
||||
"expectedAnswerModalLabel": "Verwacht antwoord",
|
||||
"validationNote": "Validatie gebeurt door vergelijking (hoofdletterongevoelig).",
|
||||
"possibleAnswersLabel": "Mogelijke antwoorden:",
|
||||
"noAnswerDefined": "Geen antwoord gedefinieerd. Voeg er minstens één toe.",
|
||||
"correctAnswer": "Correct antwoord ✓",
|
||||
"wrongAnswer": "Fout antwoord",
|
||||
"answerNote": "✓ = correct antwoord. Meerdere kunnen correct zijn.",
|
||||
"geopointZoneTitle": "Geografisch punt / Zone",
|
||||
"geometryTypeLabel": "Geometrie (Punt/Lijn/Zone):",
|
||||
"phoneModalLabel": "Telefoon",
|
||||
"categoryLabel": "Categorie:",
|
||||
"startMessageLabel": "Startbericht:",
|
||||
"startMessageModalLabel": "Startbericht",
|
||||
"createCategoryTitle": "Categorie aanmaken",
|
||||
"editCategoryTitle": "Categorie bewerken",
|
||||
"categoryIconLabel": "Categorie-icoon:",
|
||||
"selectResource": "Selecteer een resource",
|
||||
"createPdfTitle": "PDF aanmaken",
|
||||
"answersLabel": "Antwoorden",
|
||||
"answerLabel": "Antwoord",
|
||||
"createAnswerTitle": "Antwoord aanmaken",
|
||||
"editAnswerTitle": "Antwoord bewerken",
|
||||
"answerValidNote": "Indien aangevinkt, is het antwoord geldig",
|
||||
"selectConfiguration": "Selecteer een configuratie",
|
||||
"noConfigFound": "Geen configuratie gevonden",
|
||||
"backgroundColorLabel": "Achtergrondkleur:",
|
||||
"updateTabletBtn": "Tablet bijwerken",
|
||||
"kioskUpdatedSuccess": "Kiosk bijgewerkt",
|
||||
"webViewError": "De webpagina kan niet worden weergegeven, de URL is onjuist of leeg",
|
||||
"download": "Downloaden",
|
||||
"resourceDeleteConfirm": "Weet u zeker dat u deze resource wilt verwijderen?",
|
||||
"categoriesTitle": "Categorieën",
|
||||
"endTimeLabel": "Eindtijd",
|
||||
"annotationsLabel": "Annotaties"
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:manager_app/l10n/app_localizations.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_quill/flutter_quill.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
@ -234,6 +235,7 @@ class _MyAppState extends State<MyApp> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locale = Provider.of<AppContext>(context).getContext()?.locale ?? const Locale('fr');
|
||||
return MaterialApp.router(
|
||||
routerConfig: widget.router,
|
||||
key: mainKey,
|
||||
@ -246,9 +248,10 @@ class _MyAppState extends State<MyApp> {
|
||||
const Breakpoint(start: 1921, end: double.infinity, name: '4K'),
|
||||
],
|
||||
),
|
||||
locale: const Locale('fr'),
|
||||
supportedLocales: const [Locale('fr')],
|
||||
locale: locale,
|
||||
supportedLocales: const [Locale('fr'), Locale('en'), Locale('nl')],
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
FlutterQuillLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
|
||||
@ -35,6 +35,7 @@ part 'api/authentication_api.dart';
|
||||
part 'api/configuration_api.dart';
|
||||
part 'api/device_api.dart';
|
||||
part 'api/instance_api.dart';
|
||||
part 'api/subscription_plan_api.dart';
|
||||
part 'api/resource_api.dart';
|
||||
part 'api/section_api.dart';
|
||||
part 'api/section_agenda_api.dart';
|
||||
@ -116,6 +117,8 @@ part 'model/guided_step_guided_path.dart';
|
||||
part 'model/guided_step_trigger_geo_point.dart';
|
||||
part 'model/instance.dart';
|
||||
part 'model/instance_dto.dart';
|
||||
part 'model/instance_quota_dto.dart';
|
||||
part 'model/subscription_plan_dto.dart';
|
||||
part 'model/layout_main_page_type.dart';
|
||||
part 'model/login_dto.dart';
|
||||
part 'model/map_annotation.dart';
|
||||
|
||||
@ -407,4 +407,54 @@ class InstanceApi {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /api/Instance/{id}/quota' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> instanceGetQuotaWithHttpInfo(
|
||||
String id,
|
||||
) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/api/Instance/{id}/quota'.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<InstanceQuotaDTO?> instanceGetQuota(
|
||||
String id,
|
||||
) async {
|
||||
final response = await instanceGetQuotaWithHttpInfo(id);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
if (response.body.isNotEmpty &&
|
||||
response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(
|
||||
await _decodeBodyBytes(response),
|
||||
'InstanceQuotaDTO',
|
||||
) as InstanceQuotaDTO;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
219
manager_api_new/lib/api/subscription_plan_api.dart
Normal file
219
manager_api_new/lib/api/subscription_plan_api.dart
Normal file
@ -0,0 +1,219 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class SubscriptionPlanApi {
|
||||
SubscriptionPlanApi([ApiClient? apiClient])
|
||||
: apiClient = apiClient ?? defaultApiClient;
|
||||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Performs an HTTP 'GET /api/SubscriptionPlan' operation and returns the [Response].
|
||||
Future<Response> subscriptionPlanGetWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/api/SubscriptionPlan';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<SubscriptionPlanDTO>?> subscriptionPlanGet() async {
|
||||
final response = await subscriptionPlanGetWithHttpInfo();
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
if (response.body.isNotEmpty &&
|
||||
response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<SubscriptionPlanDTO>') as List)
|
||||
.cast<SubscriptionPlanDTO>()
|
||||
.toList(growable: false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /api/SubscriptionPlan/{id}' operation and returns the [Response].
|
||||
Future<Response> subscriptionPlanGetByIdWithHttpInfo(String id) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/api/SubscriptionPlan/{id}'.replaceAll('{id}', id);
|
||||
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
Future<SubscriptionPlanDTO?> subscriptionPlanGetById(String id) async {
|
||||
final response = await subscriptionPlanGetByIdWithHttpInfo(id);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
if (response.body.isNotEmpty &&
|
||||
response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(
|
||||
await _decodeBodyBytes(response),
|
||||
'SubscriptionPlanDTO',
|
||||
) as SubscriptionPlanDTO;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'POST /api/SubscriptionPlan' operation and returns the [Response].
|
||||
Future<Response> subscriptionPlanCreateWithHttpInfo(
|
||||
SubscriptionPlanDTO subscriptionPlanDTO,
|
||||
) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/api/SubscriptionPlan';
|
||||
|
||||
Object? postBody = subscriptionPlanDTO;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'POST',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
Future<SubscriptionPlanDTO?> subscriptionPlanCreate(
|
||||
SubscriptionPlanDTO subscriptionPlanDTO,
|
||||
) async {
|
||||
final response = await subscriptionPlanCreateWithHttpInfo(subscriptionPlanDTO);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
if (response.body.isNotEmpty &&
|
||||
response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(
|
||||
await _decodeBodyBytes(response),
|
||||
'SubscriptionPlanDTO',
|
||||
) as SubscriptionPlanDTO;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'PUT /api/SubscriptionPlan' operation and returns the [Response].
|
||||
Future<Response> subscriptionPlanUpdateWithHttpInfo(
|
||||
SubscriptionPlanDTO subscriptionPlanDTO,
|
||||
) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/api/SubscriptionPlan';
|
||||
|
||||
Object? postBody = subscriptionPlanDTO;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PUT',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
Future<SubscriptionPlanDTO?> subscriptionPlanUpdate(
|
||||
SubscriptionPlanDTO subscriptionPlanDTO,
|
||||
) async {
|
||||
final response = await subscriptionPlanUpdateWithHttpInfo(subscriptionPlanDTO);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
if (response.body.isNotEmpty &&
|
||||
response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(
|
||||
await _decodeBodyBytes(response),
|
||||
'SubscriptionPlanDTO',
|
||||
) as SubscriptionPlanDTO;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'DELETE /api/SubscriptionPlan/{id}' operation and returns the [Response].
|
||||
Future<Response> subscriptionPlanDeleteWithHttpInfo(String id) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/api/SubscriptionPlan/{id}'.replaceAll('{id}', id);
|
||||
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'DELETE',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> subscriptionPlanDelete(String id) async {
|
||||
final response = await subscriptionPlanDeleteWithHttpInfo(id);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
if (response.body.isNotEmpty &&
|
||||
response.statusCode != HttpStatus.noContent) {
|
||||
return await _decodeBodyBytes(response);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -363,6 +363,8 @@ class ApiClient {
|
||||
return Instance.fromJson(value);
|
||||
case 'InstanceDTO':
|
||||
return InstanceDTO.fromJson(value);
|
||||
case 'InstanceQuotaDTO':
|
||||
return InstanceQuotaDTO.fromJson(value);
|
||||
case 'LayoutMainPageType':
|
||||
return LayoutMainPageTypeTypeTransformer().decode(value);
|
||||
case 'LoginDTO':
|
||||
@ -457,6 +459,8 @@ class ApiClient {
|
||||
return Section.fromJson(value);
|
||||
case 'SectionDTO':
|
||||
return SectionDTO.fromJson(value);
|
||||
case 'SubscriptionPlanDTO':
|
||||
return SubscriptionPlanDTO.fromJson(value);
|
||||
case 'SectionEvent':
|
||||
return SectionEvent.fromJson(value);
|
||||
case 'SectionEventDTO':
|
||||
|
||||
@ -24,6 +24,15 @@ class InstanceDTO {
|
||||
this.isWeb,
|
||||
this.isVR,
|
||||
this.isAssistant,
|
||||
this.subscriptionPlanId,
|
||||
this.subscriptionPlan,
|
||||
this.aiRequestsThisMonth,
|
||||
this.aiUsageMonthKey,
|
||||
this.storageQuotaBytes,
|
||||
this.aiRequestsPerMonth,
|
||||
this.hasStats,
|
||||
this.statsHistoryDays,
|
||||
this.hasAdvancedStats,
|
||||
this.applicationInstanceDTOs = const [],
|
||||
});
|
||||
|
||||
@ -85,6 +94,24 @@ class InstanceDTO {
|
||||
|
||||
bool? isAssistant;
|
||||
|
||||
String? subscriptionPlanId;
|
||||
|
||||
SubscriptionPlanDTO? subscriptionPlan;
|
||||
|
||||
int? aiRequestsThisMonth;
|
||||
|
||||
String? aiUsageMonthKey;
|
||||
|
||||
int? storageQuotaBytes;
|
||||
|
||||
int? aiRequestsPerMonth;
|
||||
|
||||
bool? hasStats;
|
||||
|
||||
int? statsHistoryDays;
|
||||
|
||||
bool? hasAdvancedStats;
|
||||
|
||||
List<ApplicationInstanceDTO>? applicationInstanceDTOs;
|
||||
|
||||
@override
|
||||
@ -102,6 +129,15 @@ class InstanceDTO {
|
||||
other.isWeb == isWeb &&
|
||||
other.isVR == isVR &&
|
||||
other.isAssistant == isAssistant &&
|
||||
other.subscriptionPlanId == subscriptionPlanId &&
|
||||
other.subscriptionPlan == subscriptionPlan &&
|
||||
other.aiRequestsThisMonth == aiRequestsThisMonth &&
|
||||
other.aiUsageMonthKey == aiUsageMonthKey &&
|
||||
other.storageQuotaBytes == storageQuotaBytes &&
|
||||
other.aiRequestsPerMonth == aiRequestsPerMonth &&
|
||||
other.hasStats == hasStats &&
|
||||
other.statsHistoryDays == statsHistoryDays &&
|
||||
other.hasAdvancedStats == hasAdvancedStats &&
|
||||
_deepEquality.equals(
|
||||
other.applicationInstanceDTOs, applicationInstanceDTOs);
|
||||
|
||||
@ -119,11 +155,15 @@ class InstanceDTO {
|
||||
(isWeb == null ? 0 : isWeb!.hashCode) +
|
||||
(isVR == null ? 0 : isVR!.hashCode) +
|
||||
(isAssistant == null ? 0 : isAssistant!.hashCode) +
|
||||
(subscriptionPlanId == null ? 0 : subscriptionPlanId!.hashCode) +
|
||||
(subscriptionPlan == null ? 0 : subscriptionPlan!.hashCode) +
|
||||
(aiRequestsThisMonth == null ? 0 : aiRequestsThisMonth!.hashCode) +
|
||||
(aiUsageMonthKey == null ? 0 : aiUsageMonthKey!.hashCode) +
|
||||
(applicationInstanceDTOs == null ? 0 : applicationInstanceDTOs!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'InstanceDTO[id=$id, name=$name, dateCreation=$dateCreation, pinCode=$pinCode, isPushNotification=$isPushNotification, isStatistic=$isStatistic, isMobile=$isMobile, isTablet=$isTablet, isWeb=$isWeb, isVR=$isVR, isAssistant=$isAssistant, applicationInstanceDTOs=$applicationInstanceDTOs]';
|
||||
'InstanceDTO[id=$id, name=$name, dateCreation=$dateCreation, pinCode=$pinCode, isPushNotification=$isPushNotification, isStatistic=$isStatistic, isMobile=$isMobile, isTablet=$isTablet, isWeb=$isWeb, isVR=$isVR, isAssistant=$isAssistant, subscriptionPlanId=$subscriptionPlanId, subscriptionPlan=$subscriptionPlan, aiRequestsThisMonth=$aiRequestsThisMonth, aiUsageMonthKey=$aiUsageMonthKey, applicationInstanceDTOs=$applicationInstanceDTOs]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@ -182,6 +222,51 @@ class InstanceDTO {
|
||||
} else {
|
||||
json[r'isAssistant'] = null;
|
||||
}
|
||||
if (this.subscriptionPlanId != null) {
|
||||
json[r'subscriptionPlanId'] = this.subscriptionPlanId;
|
||||
} else {
|
||||
json[r'subscriptionPlanId'] = null;
|
||||
}
|
||||
if (this.subscriptionPlan != null) {
|
||||
json[r'subscriptionPlan'] = this.subscriptionPlan;
|
||||
} else {
|
||||
json[r'subscriptionPlan'] = null;
|
||||
}
|
||||
if (this.aiRequestsThisMonth != null) {
|
||||
json[r'aiRequestsThisMonth'] = this.aiRequestsThisMonth;
|
||||
} else {
|
||||
json[r'aiRequestsThisMonth'] = null;
|
||||
}
|
||||
if (this.aiUsageMonthKey != null) {
|
||||
json[r'aiUsageMonthKey'] = this.aiUsageMonthKey;
|
||||
} else {
|
||||
json[r'aiUsageMonthKey'] = null;
|
||||
}
|
||||
if (this.storageQuotaBytes != null) {
|
||||
json[r'storageQuotaBytes'] = this.storageQuotaBytes;
|
||||
} else {
|
||||
json[r'storageQuotaBytes'] = null;
|
||||
}
|
||||
if (this.aiRequestsPerMonth != null) {
|
||||
json[r'aiRequestsPerMonth'] = this.aiRequestsPerMonth;
|
||||
} else {
|
||||
json[r'aiRequestsPerMonth'] = null;
|
||||
}
|
||||
if (this.hasStats != null) {
|
||||
json[r'hasStats'] = this.hasStats;
|
||||
} else {
|
||||
json[r'hasStats'] = null;
|
||||
}
|
||||
if (this.statsHistoryDays != null) {
|
||||
json[r'statsHistoryDays'] = this.statsHistoryDays;
|
||||
} else {
|
||||
json[r'statsHistoryDays'] = null;
|
||||
}
|
||||
if (this.hasAdvancedStats != null) {
|
||||
json[r'hasAdvancedStats'] = this.hasAdvancedStats;
|
||||
} else {
|
||||
json[r'hasAdvancedStats'] = null;
|
||||
}
|
||||
if (this.applicationInstanceDTOs != null) {
|
||||
json[r'applicationInstanceDTOs'] = this.applicationInstanceDTOs;
|
||||
} else {
|
||||
@ -222,6 +307,15 @@ class InstanceDTO {
|
||||
isWeb: mapValueOfType<bool>(json, r'isWeb'),
|
||||
isVR: mapValueOfType<bool>(json, r'isVR'),
|
||||
isAssistant: mapValueOfType<bool>(json, r'isAssistant'),
|
||||
subscriptionPlanId: mapValueOfType<String>(json, r'subscriptionPlanId'),
|
||||
subscriptionPlan: SubscriptionPlanDTO.fromJson(json[r'subscriptionPlan']),
|
||||
aiRequestsThisMonth: mapValueOfType<int>(json, r'aiRequestsThisMonth'),
|
||||
aiUsageMonthKey: mapValueOfType<String>(json, r'aiUsageMonthKey'),
|
||||
storageQuotaBytes: mapValueOfType<int>(json, r'storageQuotaBytes'),
|
||||
aiRequestsPerMonth: mapValueOfType<int>(json, r'aiRequestsPerMonth'),
|
||||
hasStats: mapValueOfType<bool>(json, r'hasStats'),
|
||||
statsHistoryDays: mapValueOfType<int>(json, r'statsHistoryDays'),
|
||||
hasAdvancedStats: mapValueOfType<bool>(json, r'hasAdvancedStats'),
|
||||
applicationInstanceDTOs: ApplicationInstanceDTO.listFromJson(
|
||||
json[r'applicationInstanceDTOs']),
|
||||
);
|
||||
|
||||
106
manager_api_new/lib/model/instance_quota_dto.dart
Normal file
106
manager_api_new/lib/model/instance_quota_dto.dart
Normal file
@ -0,0 +1,106 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class InstanceQuotaDTO {
|
||||
/// Returns a new [InstanceQuotaDTO] instance.
|
||||
InstanceQuotaDTO({
|
||||
this.storageUsedBytes,
|
||||
this.storageQuotaBytes,
|
||||
this.aiRequestsUsed,
|
||||
this.aiRequestsPerMonth,
|
||||
});
|
||||
|
||||
int? storageUsedBytes;
|
||||
|
||||
int? storageQuotaBytes;
|
||||
|
||||
int? aiRequestsUsed;
|
||||
|
||||
int? aiRequestsPerMonth;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is InstanceQuotaDTO &&
|
||||
other.storageUsedBytes == storageUsedBytes &&
|
||||
other.storageQuotaBytes == storageQuotaBytes &&
|
||||
other.aiRequestsUsed == aiRequestsUsed &&
|
||||
other.aiRequestsPerMonth == aiRequestsPerMonth;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
(storageUsedBytes == null ? 0 : storageUsedBytes!.hashCode) +
|
||||
(storageQuotaBytes == null ? 0 : storageQuotaBytes!.hashCode) +
|
||||
(aiRequestsUsed == null ? 0 : aiRequestsUsed!.hashCode) +
|
||||
(aiRequestsPerMonth == null ? 0 : aiRequestsPerMonth!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'InstanceQuotaDTO[storageUsedBytes=$storageUsedBytes, storageQuotaBytes=$storageQuotaBytes, aiRequestsUsed=$aiRequestsUsed, aiRequestsPerMonth=$aiRequestsPerMonth]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.storageUsedBytes != null) {
|
||||
json[r'storageUsedBytes'] = this.storageUsedBytes;
|
||||
} else {
|
||||
json[r'storageUsedBytes'] = null;
|
||||
}
|
||||
if (this.storageQuotaBytes != null) {
|
||||
json[r'storageQuotaBytes'] = this.storageQuotaBytes;
|
||||
} else {
|
||||
json[r'storageQuotaBytes'] = null;
|
||||
}
|
||||
if (this.aiRequestsUsed != null) {
|
||||
json[r'aiRequestsUsed'] = this.aiRequestsUsed;
|
||||
} else {
|
||||
json[r'aiRequestsUsed'] = null;
|
||||
}
|
||||
if (this.aiRequestsPerMonth != null) {
|
||||
json[r'aiRequestsPerMonth'] = this.aiRequestsPerMonth;
|
||||
} else {
|
||||
json[r'aiRequestsPerMonth'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static InstanceQuotaDTO? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
return InstanceQuotaDTO(
|
||||
storageUsedBytes: mapValueOfType<int>(json, r'storageUsedBytes'),
|
||||
storageQuotaBytes: mapValueOfType<int>(json, r'storageQuotaBytes'),
|
||||
aiRequestsUsed: mapValueOfType<int>(json, r'aiRequestsUsed'),
|
||||
aiRequestsPerMonth: mapValueOfType<int>(json, r'aiRequestsPerMonth'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<InstanceQuotaDTO> listFromJson(
|
||||
dynamic json, {
|
||||
bool growable = false,
|
||||
}) {
|
||||
final result = <InstanceQuotaDTO>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = InstanceQuotaDTO.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static const requiredKeys = <String>{};
|
||||
}
|
||||
@ -38,7 +38,7 @@ class MapAnnotation {
|
||||
///
|
||||
GeometryType? geometryType;
|
||||
|
||||
MapAnnotationGeometry? geometry;
|
||||
EventAddressDTOGeometry? geometry;
|
||||
|
||||
String? polyColor;
|
||||
|
||||
@ -154,7 +154,7 @@ class MapAnnotation {
|
||||
type: TranslationDTO.listFromJson(json[r'type']),
|
||||
label: TranslationDTO.listFromJson(json[r'label']),
|
||||
geometryType: GeometryType.fromJson(json[r'geometryType']),
|
||||
geometry: MapAnnotationGeometry.fromJson(json[r'geometry']),
|
||||
geometry: EventAddressDTOGeometry.fromJson(json[r'geometry']),
|
||||
polyColor: mapValueOfType<String>(json, r'polyColor'),
|
||||
icon: mapValueOfType<String>(json, r'icon'),
|
||||
iconResourceId: mapValueOfType<String>(json, r'iconResourceId'),
|
||||
|
||||
@ -19,6 +19,7 @@ class ResourceDTO {
|
||||
this.url,
|
||||
this.dateCreation,
|
||||
this.instanceId,
|
||||
this.sizeBytes,
|
||||
});
|
||||
|
||||
String? id;
|
||||
@ -45,6 +46,8 @@ class ResourceDTO {
|
||||
|
||||
String? instanceId;
|
||||
|
||||
int? sizeBytes;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
@ -102,6 +105,9 @@ class ResourceDTO {
|
||||
} else {
|
||||
json[r'instanceId'] = null;
|
||||
}
|
||||
if (this.sizeBytes != null) {
|
||||
json[r'sizeBytes'] = this.sizeBytes;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
@ -132,6 +138,7 @@ class ResourceDTO {
|
||||
url: mapValueOfType<String>(json, r'url'),
|
||||
dateCreation: mapDateTime(json, r'dateCreation', r''),
|
||||
instanceId: mapValueOfType<String>(json, r'instanceId'),
|
||||
sizeBytes: mapValueOfType<int>(json, r'sizeBytes'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
155
manager_api_new/lib/model/subscription_plan_dto.dart
Normal file
155
manager_api_new/lib/model/subscription_plan_dto.dart
Normal file
@ -0,0 +1,155 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class SubscriptionPlanDTO {
|
||||
/// Returns a new [SubscriptionPlanDTO] instance.
|
||||
SubscriptionPlanDTO({
|
||||
this.id,
|
||||
required this.name,
|
||||
this.storageQuotaBytes,
|
||||
this.aiRequestsPerMonth,
|
||||
this.statsHistoryDays,
|
||||
this.hasAdvancedStats,
|
||||
});
|
||||
|
||||
String? id;
|
||||
|
||||
String name;
|
||||
|
||||
int? storageQuotaBytes;
|
||||
|
||||
int? aiRequestsPerMonth;
|
||||
|
||||
int? statsHistoryDays;
|
||||
|
||||
bool? hasAdvancedStats;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is SubscriptionPlanDTO &&
|
||||
other.id == id &&
|
||||
other.name == name &&
|
||||
other.storageQuotaBytes == storageQuotaBytes &&
|
||||
other.aiRequestsPerMonth == aiRequestsPerMonth &&
|
||||
other.statsHistoryDays == statsHistoryDays &&
|
||||
other.hasAdvancedStats == hasAdvancedStats;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
(id == null ? 0 : id!.hashCode) +
|
||||
(name.hashCode) +
|
||||
(storageQuotaBytes == null ? 0 : storageQuotaBytes!.hashCode) +
|
||||
(aiRequestsPerMonth == null ? 0 : aiRequestsPerMonth!.hashCode) +
|
||||
(statsHistoryDays == null ? 0 : statsHistoryDays!.hashCode) +
|
||||
(hasAdvancedStats == null ? 0 : hasAdvancedStats!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'SubscriptionPlanDTO[id=$id, name=$name, storageQuotaBytes=$storageQuotaBytes, aiRequestsPerMonth=$aiRequestsPerMonth, statsHistoryDays=$statsHistoryDays, hasAdvancedStats=$hasAdvancedStats]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.id != null) {
|
||||
json[r'id'] = this.id;
|
||||
} else {
|
||||
json[r'id'] = null;
|
||||
}
|
||||
json[r'name'] = this.name;
|
||||
if (this.storageQuotaBytes != null) {
|
||||
json[r'storageQuotaBytes'] = this.storageQuotaBytes;
|
||||
} else {
|
||||
json[r'storageQuotaBytes'] = null;
|
||||
}
|
||||
if (this.aiRequestsPerMonth != null) {
|
||||
json[r'aiRequestsPerMonth'] = this.aiRequestsPerMonth;
|
||||
} else {
|
||||
json[r'aiRequestsPerMonth'] = null;
|
||||
}
|
||||
if (this.statsHistoryDays != null) {
|
||||
json[r'statsHistoryDays'] = this.statsHistoryDays;
|
||||
} else {
|
||||
json[r'statsHistoryDays'] = null;
|
||||
}
|
||||
if (this.hasAdvancedStats != null) {
|
||||
json[r'hasAdvancedStats'] = this.hasAdvancedStats;
|
||||
} else {
|
||||
json[r'hasAdvancedStats'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SubscriptionPlanDTO? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
return SubscriptionPlanDTO(
|
||||
id: mapValueOfType<String>(json, r'id'),
|
||||
name: mapValueOfType<String>(json, r'name') ?? '',
|
||||
storageQuotaBytes: mapValueOfType<int>(json, r'storageQuotaBytes'),
|
||||
aiRequestsPerMonth: mapValueOfType<int>(json, r'aiRequestsPerMonth'),
|
||||
statsHistoryDays: mapValueOfType<int>(json, r'statsHistoryDays'),
|
||||
hasAdvancedStats: mapValueOfType<bool>(json, r'hasAdvancedStats'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SubscriptionPlanDTO> listFromJson(
|
||||
dynamic json, {
|
||||
bool growable = false,
|
||||
}) {
|
||||
final result = <SubscriptionPlanDTO>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SubscriptionPlanDTO.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SubscriptionPlanDTO> mapFromJson(dynamic json) {
|
||||
final map = <String, SubscriptionPlanDTO>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
final value = SubscriptionPlanDTO.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static Map<String, List<SubscriptionPlanDTO>> mapListFromJson(
|
||||
dynamic json, {
|
||||
bool growable = false,
|
||||
}) {
|
||||
final map = <String, List<SubscriptionPlanDTO>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SubscriptionPlanDTO.listFromJson(
|
||||
entry.value,
|
||||
growable: growable,
|
||||
);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static const requiredKeys = <String>{'name'};
|
||||
}
|
||||
@ -559,7 +559,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
|
||||
@ -23,6 +23,8 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
|
||||
rxdart:
|
||||
provider:
|
||||
@ -100,7 +102,7 @@ flutter:
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
#generate: true
|
||||
generate: true
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user