Ajout des options

This commit is contained in:
2025-05-27 22:55:34 +02:00
parent 9489183b68
commit 50a38816d3
5 changed files with 787 additions and 140 deletions

View File

@ -88,133 +88,218 @@ class EventDetails extends StatelessWidget {
),
),
const SizedBox(height: 16),
_buildInfoRow(
context,
Icons.calendar_today,
'Date de début',
dateFormat.format(event.startDateTime),
),
_buildInfoRow(
context,
Icons.calendar_today,
'Date de fin',
dateFormat.format(event.endDateTime),
),
_buildInfoRow(
context,
Icons.euro,
'Prix',
currencyFormat.format(event.price),
),
_buildInfoRow(
context,
Icons.build,
'Temps d\'installation',
'${event.installationTime} heures',
),
_buildInfoRow(
context,
Icons.construction,
'Temps de démontage',
'${event.disassemblyTime} heures',
),
const SizedBox(height: 16),
Text(
'Description',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: AppColors.noir,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
SelectableText(
event.description,
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 16),
Text(
'Adresse',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: AppColors.noir,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
SelectableText(
event.address,
style: Theme.of(context).textTheme.bodyLarge,
),
if (event.latitude != 0.0 || event.longitude != 0.0) ...[
const SizedBox(height: 4),
SelectableText(
'${event.latitude}° N, ${event.longitude}° E',
style: Theme.of(context).textTheme.bodySmall,
),
],
if (event.documents.isNotEmpty) ...[
const SizedBox(height: 16),
Text('Documents',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: AppColors.noir, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: event.documents.map((doc) {
final fileName = doc['name'] ?? '';
final url = doc['url'] ?? '';
final ext = p.extension(fileName).toLowerCase();
IconData icon;
if ([".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"]
.contains(ext)) {
icon = Icons.image;
} else if (ext == ".pdf") {
icon = Icons.picture_as_pdf;
} else if ([
".txt",
".md",
".csv",
".json",
".xml",
".docx",
".doc",
".xls",
".xlsx",
".ppt",
".pptx"
].contains(ext)) {
icon = Icons.description;
} else {
icon = Icons.attach_file;
}
return ListTile(
leading: Icon(icon, color: Colors.blueGrey),
title: SelectableText(
fileName,
maxLines: 1,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.bodyMedium,
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoRow(
context,
Icons.calendar_today,
'Date de début',
dateFormat.format(event.startDateTime),
),
trailing: IconButton(
icon: const Icon(Icons.download),
onPressed: () async {
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url),
mode: LaunchMode.externalApplication);
}
},
_buildInfoRow(
context,
Icons.calendar_today,
'Date de fin',
dateFormat.format(event.endDateTime),
),
onTap: () async {
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url),
mode: LaunchMode.externalApplication);
}
},
contentPadding: EdgeInsets.zero,
dense: true,
);
}).toList(),
_buildInfoRow(
context,
Icons.euro,
'Prix de base',
currencyFormat.format(event.basePrice),
),
if (event.options.isNotEmpty) ...[
const SizedBox(height: 8),
Text('Options sélectionnées',
style:
Theme.of(context).textTheme.titleLarge?.copyWith(
color: AppColors.noir,
fontWeight: FontWeight.bold,
)),
const SizedBox(height: 4),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: event.options.map((opt) {
final price = (opt['price'] ?? 0.0) as num;
final isNegative = price < 0;
return ListTile(
leading: Icon(Icons.tune,
color:
isNegative ? Colors.red : AppColors.rouge),
title: Text(opt['name'] ?? '',
style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(opt['details'] ?? ''),
trailing: Text(
(isNegative ? '- ' : '+ ') +
currencyFormat.format(price.abs()),
style: TextStyle(
color: isNegative ? Colors.red : AppColors.noir,
fontWeight: FontWeight.bold,
),
),
contentPadding: EdgeInsets.zero,
dense: true,
);
}).toList(),
),
const SizedBox(height: 4),
Builder(
builder: (context) {
final total = event.basePrice +
event.options.fold<num>(
0, (sum, opt) => sum + (opt['price'] ?? 0.0));
return Padding(
padding:
const EdgeInsets.only(top: 8.0, bottom: 8.0),
child: Row(
children: [
const Icon(Icons.attach_money,
color: AppColors.rouge),
const SizedBox(width: 8),
Text('Prix total : ',
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(
color: AppColors.noir,
fontWeight: FontWeight.bold,
)),
Text(
currencyFormat.format(total),
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(
color: AppColors.rouge,
fontWeight: FontWeight.bold,
),
),
],
),
);
},
),
],
_buildInfoRow(
context,
Icons.build,
'Temps d\'installation',
'${event.installationTime} heures',
),
_buildInfoRow(
context,
Icons.construction,
'Temps de démontage',
'${event.disassemblyTime} heures',
),
const SizedBox(height: 16),
Text(
'Description',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: AppColors.noir,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
SelectableText(
event.description,
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 16),
Text(
'Adresse',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: AppColors.noir,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
SelectableText(
event.address,
style: Theme.of(context).textTheme.bodyLarge,
),
if (event.latitude != 0.0 || event.longitude != 0.0) ...[
const SizedBox(height: 4),
SelectableText(
'${event.latitude}° N, ${event.longitude}° E',
style: Theme.of(context).textTheme.bodySmall,
),
],
if (event.documents.isNotEmpty) ...[
const SizedBox(height: 16),
Text('Documents',
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(
color: AppColors.noir,
fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: event.documents.map((doc) {
final fileName = doc['name'] ?? '';
final url = doc['url'] ?? '';
final ext = p.extension(fileName).toLowerCase();
IconData icon;
if ([".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"]
.contains(ext)) {
icon = Icons.image;
} else if (ext == ".pdf") {
icon = Icons.picture_as_pdf;
} else if ([
".txt",
".md",
".csv",
".json",
".xml",
".docx",
".doc",
".xls",
".xlsx",
".ppt",
".pptx"
].contains(ext)) {
icon = Icons.description;
} else {
icon = Icons.attach_file;
}
return ListTile(
leading: Icon(icon, color: Colors.blueGrey),
title: SelectableText(
fileName,
maxLines: 1,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.bodyMedium,
),
trailing: IconButton(
icon: const Icon(Icons.download),
onPressed: () async {
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url),
mode: LaunchMode.externalApplication);
}
},
),
onTap: () async {
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url),
mode: LaunchMode.externalApplication);
}
},
contentPadding: EdgeInsets.zero,
dense: true,
);
}).toList(),
),
],
],
),
),
],
),
],
),
),
@ -303,7 +388,7 @@ class _EventAddDialogState extends State<EventAddDialog> {
description: _descriptionController.text.trim(),
startDateTime: _startDateTime!,
endDateTime: _endDateTime!,
price: double.tryParse(_priceController.text) ?? 0.0,
basePrice: double.tryParse(_priceController.text) ?? 0.0,
installationTime: int.tryParse(_installationController.text) ?? 0,
disassemblyTime: int.tryParse(_disassemblyController.text) ?? 0,
eventTypeId: '', // à adapter si tu veux gérer les types