312 lines
10 KiB
Dart
312 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:manager_app/Components/fetch_section_icon.dart';
|
|
import 'package:manager_app/l10n/app_localizations.dart';
|
|
import 'package:manager_app/Components/message_notification.dart';
|
|
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_api_new/api.dart';
|
|
|
|
/// Les 13 types, groupés par famille. C'est la première interface que voit un
|
|
/// nouveau client : un menu déroulant demandait de connaître les noms avant de
|
|
/// choisir, la grille les montre.
|
|
const Map<String, List<SectionType>> _sectionFamilies = {
|
|
"content": [
|
|
SectionType.Slider,
|
|
SectionType.Article,
|
|
SectionType.Pdf,
|
|
SectionType.Video,
|
|
SectionType.Web,
|
|
],
|
|
"places": [
|
|
SectionType.Map,
|
|
SectionType.Parcours,
|
|
SectionType.Event,
|
|
],
|
|
"interactive": [
|
|
SectionType.Quiz,
|
|
SectionType.Game,
|
|
SectionType.Menu,
|
|
],
|
|
"practical": [
|
|
SectionType.Agenda,
|
|
SectionType.Weather,
|
|
],
|
|
};
|
|
|
|
String _familyLabel(AppLocalizations l, String family) {
|
|
switch (family) {
|
|
case "content": return l.sectionFamilyContent;
|
|
case "places": return l.sectionFamilyPlaces;
|
|
case "interactive": return l.sectionFamilyInteractive;
|
|
default: return l.sectionFamilyPractical;
|
|
}
|
|
}
|
|
|
|
Future<SectionDTO?> showNewSection(String configurationId,
|
|
AppContext appContext, BuildContext contextBuild, bool isSubSection) {
|
|
SectionDTO sectionDTO = new SectionDTO();
|
|
sectionDTO.label = "";
|
|
sectionDTO.configurationId = configurationId;
|
|
sectionDTO.isSubSection = isSubSection;
|
|
sectionDTO.isActive = true;
|
|
sectionDTO.parentId = isSubSection
|
|
? (appContext.getContext() as ManagerAppContext).selectedSection!.id
|
|
: null;
|
|
sectionDTO.type = SectionType.Map;
|
|
|
|
return showDialog<SectionDTO?>(
|
|
context: contextBuild,
|
|
builder: (BuildContext context) => _NewSectionDialog(
|
|
sectionDTO: sectionDTO,
|
|
isSubSection: isSubSection,
|
|
),
|
|
);
|
|
}
|
|
|
|
class _NewSectionDialog extends StatefulWidget {
|
|
const _NewSectionDialog({required this.sectionDTO, required this.isSubSection});
|
|
|
|
final SectionDTO sectionDTO;
|
|
final bool isSubSection;
|
|
|
|
@override
|
|
State<_NewSectionDialog> createState() => _NewSectionDialogState();
|
|
}
|
|
|
|
class _NewSectionDialogState extends State<_NewSectionDialog> {
|
|
late SectionType selectedType = widget.sectionDTO.type ?? SectionType.Map;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l = AppLocalizations.of(context)!;
|
|
|
|
return Dialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(kRadiusShell),
|
|
side: const BorderSide(color: kLine),
|
|
),
|
|
clipBehavior: Clip.antiAlias,
|
|
child: SizedBox(
|
|
width: 780,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(kSpace6, kSpace5, kSpace4, kSpace5),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
widget.isSubSection ? l.newSubSection : l.newSection,
|
|
style: kTitleCard,
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.close, size: 20, color: kInk3),
|
|
tooltip: l.cancel,
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const Divider(height: 1, thickness: 1, color: kLineSoft),
|
|
Flexible(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(kSpace6),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(
|
|
width: 340,
|
|
child: StringInputContainer(
|
|
label: l.sectionNameLabel,
|
|
isMandatory: true,
|
|
initialValue: widget.sectionDTO.label,
|
|
onChanged: (value) {
|
|
widget.sectionDTO.label = value;
|
|
},
|
|
),
|
|
),
|
|
for (final family in _sectionFamilies.entries)
|
|
..._buildFamily(l, family.key, family.value),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.all(kSpace4),
|
|
decoration: const BoxDecoration(
|
|
color: kSurface2,
|
|
border: Border(top: BorderSide(color: kLineSoft)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(l.sectionTypeFinalWarning, style: kTextHint),
|
|
),
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: Text(l.cancel,
|
|
style: const TextStyle(fontSize: 13, color: kInk2)),
|
|
),
|
|
const SizedBox(width: kSpace2),
|
|
FilledButton.icon(
|
|
onPressed: _submit,
|
|
icon: const Icon(Icons.add, size: 16),
|
|
label: Text(l.create,
|
|
style: const TextStyle(
|
|
fontSize: 13, fontWeight: FontWeight.w600)),
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: kPrimaryColor,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: kSpace5, vertical: kSpace3),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(kRadiusPill)),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
List<Widget> _buildFamily(
|
|
AppLocalizations l, String family, List<SectionType> types) {
|
|
// Une sous-section ne peut pas être un Menu : un menu regroupe des sous-sections.
|
|
final available = widget.isSubSection
|
|
? types.where((type) => type != SectionType.Menu).toList()
|
|
: types;
|
|
if (available.isEmpty) return [];
|
|
|
|
return [
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: kSpace7, bottom: kSpace3),
|
|
child: Text(_familyLabel(l, family), style: kOverline),
|
|
),
|
|
LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
const spacing = kSpace4;
|
|
const minWidth = 178.0;
|
|
final columns =
|
|
((constraints.maxWidth + spacing) / (minWidth + spacing))
|
|
.floor()
|
|
.clamp(1, available.length);
|
|
final width =
|
|
(constraints.maxWidth - spacing * (columns - 1)) / columns;
|
|
|
|
return Wrap(
|
|
spacing: spacing,
|
|
runSpacing: spacing,
|
|
children: available
|
|
.map((type) => SizedBox(
|
|
width: width,
|
|
child: _TypeCard(
|
|
type: type,
|
|
isSelected: type == selectedType,
|
|
onTap: () => setState(() {
|
|
selectedType = type;
|
|
widget.sectionDTO.type = type;
|
|
}),
|
|
),
|
|
))
|
|
.toList(),
|
|
);
|
|
},
|
|
),
|
|
];
|
|
}
|
|
|
|
void _submit() {
|
|
final l = AppLocalizations.of(context)!;
|
|
if (widget.sectionDTO.label != null &&
|
|
widget.sectionDTO.label!.length > 2) {
|
|
Navigator.of(context).pop(widget.sectionDTO);
|
|
} else {
|
|
showNotification(
|
|
Colors.orange, kWhite, l.sectionNameRequired, context, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
class _TypeCard extends StatelessWidget {
|
|
const _TypeCard({
|
|
required this.type,
|
|
required this.isSelected,
|
|
required this.onTap,
|
|
});
|
|
|
|
final SectionType type;
|
|
final bool isSelected;
|
|
final VoidCallback onTap;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l = AppLocalizations.of(context)!;
|
|
|
|
return Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(kRadiusCard),
|
|
onTap: onTap,
|
|
child: Container(
|
|
padding: const EdgeInsets.all(kSpace4),
|
|
decoration: BoxDecoration(
|
|
color: isSelected ? kSurface3 : kSurface,
|
|
border: Border.all(
|
|
color: isSelected ? kPrimaryColor : kLine,
|
|
width: isSelected ? 1.5 : 1,
|
|
),
|
|
borderRadius: BorderRadius.circular(kRadiusCard),
|
|
),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Icon(getSectionIcon(type),
|
|
size: 20, color: isSelected ? kPrimaryColor : kInk3),
|
|
const SizedBox(width: kSpace3),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(getSectionTypeName(l, type), style: kTextSmall),
|
|
const SizedBox(height: 2),
|
|
Text(getSectionTypeDescription(l, type),
|
|
style: kTextHint, maxLines: 2),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
void create(SectionDTO sectionDTO, AppContext appContext, BuildContext context,
|
|
bool isSubSection, Function? sendSubSection) async {
|
|
if (sectionDTO.label != null) {
|
|
ManagerAppContext managerAppContext = appContext.getContext();
|
|
sectionDTO.instanceId = managerAppContext.instanceId;
|
|
sectionDTO.isBeacon = false;
|
|
sectionDTO.dateCreation = DateTime.now();
|
|
SectionDTO? newSection = await managerAppContext.clientAPI!.sectionApi!
|
|
.sectionCreate(sectionDTO);
|
|
|
|
if (!isSubSection) {
|
|
appContext.setContext(managerAppContext);
|
|
showNotification(kSuccess, kWhite, AppLocalizations.of(context)!.sectionCreatedSuccess,
|
|
context, null);
|
|
} else {
|
|
sendSubSection!(newSection);
|
|
}
|
|
Navigator.of(context).pop();
|
|
}
|
|
}
|