Files
unionflow-server-impl-quarkus/unionflow-mobile-apps/lib/features/events/presentation/pages/event_detail_page.dart

407 lines
12 KiB
Dart

/// Page de détails d'un événement
library event_detail_page;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../bloc/evenements_bloc.dart';
import '../../bloc/evenements_state.dart';
import '../../data/models/evenement_model.dart';
import '../widgets/inscription_event_dialog.dart';
import '../widgets/edit_event_dialog.dart';
class EventDetailPage extends StatelessWidget {
final EvenementModel evenement;
const EventDetailPage({
super.key,
required this.evenement,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Détails de l\'événement'),
backgroundColor: const Color(0xFF3B82F6),
foregroundColor: Colors.white,
actions: [
IconButton(
icon: const Icon(Icons.edit),
onPressed: () => _showEditDialog(context),
),
],
),
body: BlocBuilder<EvenementsBloc, EvenementsState>(
builder: (context, state) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(),
_buildInfoSection(),
_buildDescriptionSection(),
if (evenement.lieu != null) _buildLocationSection(),
_buildParticipantsSection(),
const SizedBox(height: 80), // Espace pour le bouton flottant
],
),
);
},
),
floatingActionButton: _buildInscriptionButton(context),
);
}
Widget _buildHeader() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
const Color(0xFF3B82F6),
const Color(0xFF3B82F6).withOpacity(0.8),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Text(
_getTypeLabel(evenement.type),
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 12),
Text(
evenement.titre,
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _getStatutColor(evenement.statut),
borderRadius: BorderRadius.circular(4),
),
child: Text(
_getStatutLabel(evenement.statut),
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
);
}
Widget _buildInfoSection() {
return Container(
padding: const EdgeInsets.all(16),
child: Column(
children: [
_buildInfoRow(
Icons.calendar_today,
'Date de début',
_formatDate(evenement.dateDebut),
),
const Divider(),
_buildInfoRow(
Icons.event,
'Date de fin',
_formatDate(evenement.dateFin),
),
if (evenement.maxParticipants != null) ...[
const Divider(),
_buildInfoRow(
Icons.people,
'Places',
'${evenement.participantsActuels} / ${evenement.maxParticipants}',
),
],
if (evenement.organisateurNom != null) ...[
const Divider(),
_buildInfoRow(
Icons.person,
'Organisateur',
evenement.organisateurNom!,
),
],
],
),
);
}
Widget _buildInfoRow(IconData icon, String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Icon(icon, color: const Color(0xFF3B82F6), size: 20),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
const SizedBox(height: 2),
Text(
value,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
);
}
Widget _buildDescriptionSection() {
if (evenement.description == null) return const SizedBox.shrink();
return Container(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Description',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
evenement.description!,
style: const TextStyle(fontSize: 14, height: 1.5),
),
],
),
);
}
Widget _buildLocationSection() {
return Container(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Lieu',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.location_on, color: Color(0xFF3B82F6)),
const SizedBox(width: 8),
Expanded(
child: Text(
evenement.lieu!,
style: const TextStyle(fontSize: 14),
),
),
],
),
],
),
);
}
Widget _buildParticipantsSection() {
return Container(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Participants',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
'${evenement.participantsActuels} inscrits',
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
),
],
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: const Row(
children: [
Icon(Icons.info_outline, size: 20),
SizedBox(width: 8),
Expanded(
child: Text(
'La liste des participants est visible uniquement pour les organisateurs',
style: TextStyle(fontSize: 12),
),
),
],
),
),
],
),
);
}
Widget _buildInscriptionButton(BuildContext context) {
const isInscrit = false; // TODO: Vérifier si l'utilisateur est inscrit
final placesRestantes = (evenement.maxParticipants ?? 0) -
evenement.participantsActuels;
final isComplet = placesRestantes <= 0 && evenement.maxParticipants != null;
return FloatingActionButton.extended(
onPressed: (isInscrit || !isComplet)
? () => _showInscriptionDialog(context, isInscrit)
: null,
backgroundColor: isInscrit ? Colors.red : const Color(0xFF3B82F6),
icon: Icon(isInscrit ? Icons.cancel : Icons.check),
label: Text(
isInscrit ? 'Se désinscrire' : (isComplet ? 'Complet' : 'S\'inscrire'),
),
);
}
void _showInscriptionDialog(BuildContext context, bool isInscrit) {
showDialog(
context: context,
builder: (context) => BlocProvider.value(
value: context.read<EvenementsBloc>(),
child: InscriptionEventDialog(
evenement: evenement,
isInscrit: isInscrit,
),
),
);
}
void _showEditDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => BlocProvider.value(
value: context.read<EvenementsBloc>(),
child: EditEventDialog(evenement: evenement),
),
);
}
String _formatDate(DateTime date) {
final months = [
'janvier', 'février', 'mars', 'avril', 'mai', 'juin',
'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'
];
return '${date.day} ${months[date.month - 1]} ${date.year} à ${date.hour}:${date.minute.toString().padLeft(2, '0')}';
}
String _getTypeLabel(TypeEvenement type) {
switch (type) {
case TypeEvenement.assembleeGenerale:
return 'Assemblée Générale';
case TypeEvenement.reunion:
return 'Réunion';
case TypeEvenement.formation:
return 'Formation';
case TypeEvenement.conference:
return 'Conférence';
case TypeEvenement.atelier:
return 'Atelier';
case TypeEvenement.seminaire:
return 'Séminaire';
case TypeEvenement.evenementSocial:
return 'Événement Social';
case TypeEvenement.manifestation:
return 'Manifestation';
case TypeEvenement.celebration:
return 'Célébration';
case TypeEvenement.autre:
return 'Autre';
}
}
String _getStatutLabel(StatutEvenement statut) {
switch (statut) {
case StatutEvenement.planifie:
return 'Planifié';
case StatutEvenement.confirme:
return 'Confirmé';
case StatutEvenement.enCours:
return 'En cours';
case StatutEvenement.termine:
return 'Terminé';
case StatutEvenement.annule:
return 'Annulé';
case StatutEvenement.reporte:
return 'Reporté';
}
}
Color _getStatutColor(StatutEvenement statut) {
switch (statut) {
case StatutEvenement.planifie:
return Colors.blue;
case StatutEvenement.confirme:
return Colors.green;
case StatutEvenement.enCours:
return Colors.orange;
case StatutEvenement.termine:
return Colors.grey;
case StatutEvenement.annule:
return Colors.red;
case StatutEvenement.reporte:
return Colors.purple;
}
}
}