diff --git a/lib/core/constants/urls.dart b/lib/core/constants/urls.dart index 7c3aaaa..6d3a25b 100644 --- a/lib/core/constants/urls.dart +++ b/lib/core/constants/urls.dart @@ -1,5 +1,5 @@ class Urls { - static const String baseUrl = 'http://192.168.1.14:8085'; + static const String baseUrl = 'http://192.168.0.145:8085'; // static const String login = baseUrl + 'auth/login'; static const String eventsUrl = '$baseUrl/events'; // Ajoute d'autres URLs ici diff --git a/lib/data/datasources/event_remote_data_source.dart b/lib/data/datasources/event_remote_data_source.dart index 4ebf539..186a473 100644 --- a/lib/data/datasources/event_remote_data_source.dart +++ b/lib/data/datasources/event_remote_data_source.dart @@ -168,4 +168,29 @@ class EventRemoteDataSource { throw ServerExceptionWithMessage('Une erreur est survenue lors de la fermeture de l\'événement.'); } } + + /// Rouvrir un événement. + Future reopenEvent(String eventId) async { + print('Réouverture de l\'événement avec l\'ID: $eventId'); + + final response = await client.post( + Uri.parse('${Urls.eventsUrl}/$eventId/reopen'), + headers: {'Content-Type': 'application/json'}, + ); + + print('Statut de la réponse: ${response.statusCode}'); + + if (response.statusCode == 200) { + print('Événement rouvert avec succès'); + } else if (response.statusCode == 400) { + // Si le serveur retourne une erreur 400, vérifiez le corps du message + final responseBody = json.decode(response.body); + final errorMessage = responseBody['message'] ?? 'Erreur inconnue'; + print('Erreur lors de la réouverture de l\'événement: $errorMessage'); + throw ServerExceptionWithMessage(errorMessage); + } else { + print('Erreur lors de la réouverture de l\'événement: ${response.body}'); + throw ServerExceptionWithMessage('Une erreur est survenue lors de la réouverture de l\'événement.'); + } + } } diff --git a/lib/data/models/event_model.dart b/lib/data/models/event_model.dart index ab81dd8..7e6ca5a 100644 --- a/lib/data/models/event_model.dart +++ b/lib/data/models/event_model.dart @@ -1,18 +1,21 @@ import 'package:afterwork/data/models/user_model.dart'; /// Modèle de données représentant un événement. +/// Cette classe encapsule toutes les propriétés d'un événement, y compris +/// le titre, la description, la date, la localisation, la catégorie, les liens, +/// l'image, le créateur, les participants et le statut de l'événement. class EventModel { - final String id; - final String title; - final String description; - final String date; - final String location; - final String category; - final String link; - final String? imageUrl; - final UserModel creator; - final List participants; - final String status; + final String id; // Identifiant unique de l'événement + final String title; // Titre de l'événement + final String description; // Description de l'événement + final String date; // Date de l'événement + final String location; // Localisation de l'événement + final String category; // Catégorie de l'événement + final String link; // Lien associé à l'événement + final String? imageUrl; // URL de l'image de l'événement (optionnel) + final UserModel creator; // Créateur de l'événement + final List participants; // Liste des participants à l'événement + final String status; // Statut de l'événement (e.g., "OPEN", "CLOSED") /// Constructeur pour initialiser toutes les propriétés de l'événement. EventModel({ @@ -31,6 +34,9 @@ class EventModel { /// Convertit un objet JSON en `EventModel`. factory EventModel.fromJson(Map json) { + // Log de la conversion depuis JSON + print('Conversion de l\'objet JSON en EventModel: ${json['id']}'); + return EventModel( id: json['id'], title: json['title'], @@ -52,6 +58,9 @@ class EventModel { /// Convertit un `EventModel` en objet JSON. Map toJson() { + // Log de la conversion en JSON + print('Conversion de l\'EventModel en objet JSON: $id'); + return { 'id': id, 'title': title, @@ -63,6 +72,7 @@ class EventModel { 'imageUrl': imageUrl, 'creator': creator.toJson(), 'participants': participants.map((user) => user.toJson()).toList(), + 'status': status, }; } } diff --git a/lib/presentation/screens/event/event_card.dart b/lib/presentation/screens/event/event_card.dart index 8e442ee..f1cd88d 100644 --- a/lib/presentation/screens/event/event_card.dart +++ b/lib/presentation/screens/event/event_card.dart @@ -4,7 +4,7 @@ import 'package:afterwork/data/datasources/event_remote_data_source.dart'; /// Widget pour afficher une carte d'événement. /// Cette classe est utilisée pour afficher les détails d'un événement, /// incluant son titre, sa description, son image, et des actions possibles -/// telles que réagir, commenter, partager, participer, et fermer l'événement. +/// telles que réagir, commenter, partager, participer, et fermer ou rouvrir l'événement. class EventCard extends StatelessWidget { // Identifiant unique de l'événement final String eventId; @@ -50,6 +50,8 @@ class EventCard extends StatelessWidget { final VoidCallback onMoreOptions; // Callback pour fermer l'événement final VoidCallback onCloseEvent; + // Callback pour rouvrir l'événement + final VoidCallback onReopenEvent; const EventCard({ Key? key, @@ -75,6 +77,7 @@ class EventCard extends StatelessWidget { required this.onParticipate, required this.onMoreOptions, required this.onCloseEvent, + required this.onReopenEvent, }) : super(key: key); @override @@ -87,11 +90,19 @@ class EventCard extends StatelessWidget { duration: const Duration(milliseconds: 300), child: Dismissible( key: ValueKey(eventId), - direction: DismissDirection.startToEnd, + direction: eventStatus == 'CLOSED' + ? DismissDirection.endToStart // Permet de rouvrir avec un swipe à gauche + : DismissDirection.startToEnd, // Permet de fermer avec un swipe à droite onDismissed: (direction) { - // Log du déclenchement de la fermeture de l'événement - print('Tentative de fermeture de l\'événement $eventId'); - onCloseEvent(); + if (direction == DismissDirection.startToEnd) { + // Log du déclenchement de la fermeture de l'événement + print('Tentative de fermeture de l\'événement $eventId'); + onCloseEvent(); + } else if (direction == DismissDirection.endToStart && eventStatus == 'CLOSED') { + // Log du déclenchement de la réouverture de l'événement + print('Tentative de réouverture de l\'événement $eventId'); + onReopenEvent(); + } }, background: Container( color: Colors.red, @@ -99,6 +110,12 @@ class EventCard extends StatelessWidget { padding: const EdgeInsets.only(left: 20.0), child: const Icon(Icons.delete, color: Colors.white), ), + secondaryBackground: Container( + color: Colors.green, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 20.0), + child: const Icon(Icons.replay, color: Colors.white), + ), child: Stack( children: [ Card( @@ -114,7 +131,7 @@ class EventCard extends StatelessWidget { children: [ _buildHeader(context), const SizedBox(height: 10), - _buildEventCategory(), // Affichage de la catégorie de l'événement + _buildEventCategory(), const SizedBox(height: 5), _buildEventDetails(), const SizedBox(height: 10), @@ -271,11 +288,11 @@ class EventCard extends StatelessWidget { print('Affichage des interactions pour l\'événement $eventId'); return Padding( - padding: const EdgeInsets.symmetric(horizontal: 0.0), // Réduire le padding vertical + padding: const EdgeInsets.symmetric(vertical: 4.0), // Réduire le padding vertical child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, // Utiliser spaceAround pour réduire l'espace children: [ - Flexible( + Expanded( child: _buildIconButton( icon: Icons.thumb_up_alt_outlined, label: 'Réagir', @@ -287,7 +304,7 @@ class EventCard extends StatelessWidget { }, ), ), - Flexible( + Expanded( child: _buildIconButton( icon: Icons.comment_outlined, label: 'Commenter', @@ -299,7 +316,7 @@ class EventCard extends StatelessWidget { }, ), ), - Flexible( + Expanded( child: _buildIconButton( icon: Icons.share_outlined, label: 'Partager', @@ -327,28 +344,33 @@ class EventCard extends StatelessWidget { // Log de la construction du bouton d'interaction print('Construction du bouton $label pour l\'événement $eventId'); - return Expanded( - child: TextButton.icon( - onPressed: onPressed, - icon: Icon(icon, color: const Color(0xFF1DBF73), size: 20), - label: Text( - '$label ($count)', - style: const TextStyle(color: Colors.white70, fontSize: 12), - overflow: TextOverflow.ellipsis, - ), + return TextButton.icon( + onPressed: onPressed, + icon: Icon(icon, color: const Color(0xFF1DBF73), size: 20), + label: Text( + '$label ($count)', + style: const TextStyle(color: Colors.white70, fontSize: 12), + overflow: TextOverflow.ellipsis, ), ); } /// Bouton pour participer à l'événement. /// Cette méthode construit un bouton qui permet de participer à l'événement. - /// Si l'événement est fermé, le bouton est désactivé. + /// Si l'événement est fermé, le bouton est caché. Widget _buildParticipateButton() { // Log de la construction du bouton "Participer" print('Construction du bouton "Participer" pour l\'événement $eventId avec statut $eventStatus'); + // Si l'événement est fermé, ne rien retourner (pas de bouton) + if (eventStatus == 'CLOSED') { + print('L\'événement $eventId est fermé, le bouton "Participer" est caché.'); + return SizedBox.shrink(); // Retourne un widget vide pour ne pas occuper d'espace + } + + // Sinon, retourner le bouton "Participer" return ElevatedButton( - onPressed: eventStatus == 'CLOSED' ? null : onParticipate, // Désactiver si l'événement est fermé + onPressed: onParticipate, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF1DBF73), shape: RoundedRectangleBorder( @@ -357,9 +379,9 @@ class EventCard extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 12.0), minimumSize: const Size(double.infinity, 40), ), - child: Text( - eventStatus == 'CLOSED' ? 'Événement fermé' : 'Participer', - style: const TextStyle(color: Colors.white), + child: const Text( + 'Participer', + style: TextStyle(color: Colors.white), ), ); } diff --git a/lib/presentation/screens/event/event_screen.dart b/lib/presentation/screens/event/event_screen.dart index 4867e95..ff0c5f6 100644 --- a/lib/presentation/screens/event/event_screen.dart +++ b/lib/presentation/screens/event/event_screen.dart @@ -29,6 +29,7 @@ class _EventScreenState extends State { @override void initState() { super.initState(); + // Récupérer la liste des événements à partir de la source de données distante _eventsFuture = widget.eventRemoteDataSource.getAllEvents(); } @@ -59,7 +60,6 @@ class _EventScreenState extends State { ); if (eventData != null) { - // Appeler l'API pour créer un nouvel événement. try { print('Tentative de création d\'un nouvel événement par l\'utilisateur ${widget.userId}'); await widget.eventRemoteDataSource.createEvent(eventData as EventModel); @@ -104,7 +104,7 @@ class _EventScreenState extends State { print('Affichage de l\'événement ${event.id}'); return EventCard( - key: ValueKey(event.id), // Pour une gestion correcte du Key dans l'animation + key: ValueKey(event.id), eventRemoteDataSource: widget.eventRemoteDataSource, userId: widget.userId, eventId: event.id, @@ -117,7 +117,7 @@ class _EventScreenState extends State { eventTitle: event.title, eventDescription: event.description, eventImageUrl: event.imageUrl ?? 'lib/assets/images/placeholder.png', - eventStatus: event.status, // Ajouter le statut de l'événement ici + eventStatus: event.status, reactionsCount: 120, // Exemple de valeur commentsCount: 45, // Exemple de valeur sharesCount: 30, // Exemple de valeur @@ -137,6 +137,7 @@ class _EventScreenState extends State { onMoreOptions: () { print('Affichage des options pour l\'événement ${event.id}'); }, + onReopenEvent: () => _onReopenEvent(context, event.id, index), ); }, ); @@ -150,18 +151,21 @@ class _EventScreenState extends State { void _onCloseEvent(BuildContext context, String eventId, int index) async { try { print('Tentative de fermeture de l\'événement $eventId'); + + // Appeler l'API pour fermer l'événement await widget.eventRemoteDataSource.closeEvent(eventId); print('Événement fermé avec succès'); - // Montrer un message de succès + // Montrer un message de succès AVANT de supprimer l'événement de la liste ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('L\'événement a été fermé avec succès.')), ); - // Actualiser la liste des événements après fermeture avec un effet de fondu + // Supprimez l'événement de la liste après avoir affiché le SnackBar setState(() { - _eventsFuture.then((events) { + _eventsFuture = _eventsFuture.then((events) { events.removeAt(index); + return events; }); }); } catch (e) { @@ -171,4 +175,46 @@ class _EventScreenState extends State { ); } } + + /// Logique pour rouvrir un événement + void _onReopenEvent(BuildContext context, String eventId, int index) async { + try { + print('Tentative de réouverture de l\'événement $eventId'); + await widget.eventRemoteDataSource.reopenEvent(eventId); + print('Événement rouvert avec succès'); + + // Montrer un message de succès + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('L\'événement a été rouvert avec succès.')), + ); + + // Mettre à jour le statut de l'événement dans la liste des événements + setState(() { + _eventsFuture = _eventsFuture.then((events) { + final updatedEvent = EventModel( + id: events[index].id, + title: events[index].title, + description: events[index].description, + date: events[index].date, + location: events[index].location, + category: events[index].category, + link: events[index].link, + imageUrl: events[index].imageUrl, + creator: events[index].creator, + participants: events[index].participants, + status: 'OPEN', // Mettre à jour le statut à 'OPEN' + ); + + // Remplacer l'événement dans la liste + events[index] = updatedEvent; + return events; + }); + }); + } catch (e) { + print('Erreur lors de la réouverture de l\'événement: $e'); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Erreur lors de la réouverture de l\'événement : $e')), + ); + } + } }