Files
afterwork/lib/presentation/screens/dialogs/add_event_dialog.dart
dahoud 92612abbd7 fix(chat): Correction race condition + Implémentation TODOs
## Corrections Critiques

### Race Condition - Statuts de Messages
- Fix : Les icônes de statut (✓, ✓✓, ✓✓ bleu) ne s'affichaient pas
- Cause : WebSocket delivery confirmations arrivaient avant messages locaux
- Solution : Pattern Optimistic UI dans chat_bloc.dart
  - Création message temporaire immédiate
  - Ajout à la liste AVANT requête HTTP
  - Remplacement par message serveur à la réponse
- Fichier : lib/presentation/state_management/chat_bloc.dart

## Implémentation TODOs (13/21)

### Social (social_header_widget.dart)
-  Copier lien du post dans presse-papiers
-  Partage natif via Share.share()
-  Dialogue de signalement avec 5 raisons

### Partage (share_post_dialog.dart)
-  Interface sélection d'amis avec checkboxes
-  Partage externe via Share API

### Média (media_upload_service.dart)
-  Parsing JSON réponse backend
-  Méthode deleteMedia() pour suppression
-  Génération miniature vidéo

### Posts (create_post_dialog.dart, edit_post_dialog.dart)
-  Extraction URL depuis uploads
-  Documentation chargement médias

### Chat (conversations_screen.dart)
-  Navigation vers notifications
-  ConversationSearchDelegate pour recherche

## Nouveaux Fichiers

### Configuration
- build-prod.ps1 : Script build production avec dart-define
- lib/core/constants/env_config.dart : Gestion environnements

### Documentation
- TODOS_IMPLEMENTED.md : Documentation complète TODOs

## Améliorations

### Architecture
- Refactoring injection de dépendances
- Amélioration routing et navigation
- Optimisation providers (UserProvider, FriendsProvider)

### UI/UX
- Amélioration thème et couleurs
- Optimisation animations
- Meilleure gestion erreurs

### Services
- Configuration API avec env_config
- Amélioration datasources (events, users)
- Optimisation modèles de données
2026-01-10 10:43:17 +00:00

225 lines
9.0 KiB
Dart

import 'dart:io'; // Pour l'usage des fichiers (image)
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../core/utils/app_logger.dart';
import '../../widgets/date_picker.dart';
import '../../widgets/fields/accessibility_field.dart';
import '../../widgets/fields/accommodation_info_field.dart';
import '../../widgets/fields/attendees_field.dart';
import '../../widgets/fields/category_field.dart'; // Importation des widgets personnalisés
import '../../widgets/fields/description_field.dart';
import '../../widgets/fields/link_field.dart';
import '../../widgets/fields/location_field.dart';
import '../../widgets/fields/organizer_field.dart';
import '../../widgets/fields/parking_field.dart';
import '../../widgets/fields/participation_fee_field.dart';
import '../../widgets/fields/privacy_rules_field.dart';
import '../../widgets/fields/security_protocol_field.dart';
import '../../widgets/fields/tags_field.dart';
import '../../widgets/fields/title_field.dart';
import '../../widgets/fields/transport_info_field.dart';
import '../../widgets/image_preview_picker.dart';
import '../../widgets/submit_button.dart';
/// Page pour ajouter un événement
/// Permet à l'utilisateur de remplir un formulaire avec des détails sur l'événement
class AddEventPage extends StatefulWidget {
const AddEventPage({
required this.userId, required this.userFirstName, required this.userLastName, super.key,
});
final String userId;
final String userFirstName;
final String userLastName;
@override
_AddEventPageState createState() => _AddEventPageState();
}
class _AddEventPageState extends State<AddEventPage> {
final _formKey = GlobalKey<FormState>(); // Clé pour la validation du formulaire
// Variables pour stocker les données de l'événement
String _title = '';
String _description = '';
DateTime? _selectedDate;
DateTime? _endDate;
String _location = 'Abidjan';
String _category = '';
String _link = '';
String _organizer = '';
List<String> _tags = [];
int _maxParticipants = 0;
final LatLng _selectedLatLng = const LatLng(5.348722, -3.985038); // Coordonnées par défaut
File? _selectedImageFile; // Image sélectionnée
final String _status = 'Actif';
final String _organizerEmail = '';
final String _organizerPhone = '';
int _participationFee = 0;
String _privacyRules = '';
String _transportInfo = '';
String _accommodationInfo = '';
final bool _isAccessible = false;
bool _hasParking = false;
String _securityProtocol = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Créer un événement'),
backgroundColor: const Color(0xFF1E1E2C),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
),
backgroundColor: const Color(0xFF1E1E2C),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Section d'informations de base
_buildSectionHeader('Informations de base'),
ImagePreviewPicker(
onImagePicked: (File? imageFile) {
setState(() {
_selectedImageFile = imageFile;
});
},
),
const SizedBox(height: 20),
TitleField(onSaved: (value) => setState(() => _title = value ?? '')),
const SizedBox(height: 12),
DescriptionField(onSaved: (value) => setState(() => _description = value ?? '')),
const SizedBox(height: 12),
DatePickerField(
selectedDate: _selectedDate,
onDatePicked: (picked) => setState(() {
_selectedDate = picked;
}),
label: 'Date de début',
),
const SizedBox(height: 12),
DatePickerField(
selectedDate: _endDate,
onDatePicked: (picked) => setState(() {
_endDate = picked;
}),
label: 'Date de fin',
),
const SizedBox(height: 12),
LocationField(
location: _location,
onLocationPicked: (value) => setState(() => _location = (value ?? 'Abidjan') as String),
),
const SizedBox(height: 12),
CategoryField(
onSaved: (value) => setState(() => _category = value ?? ''),
),
const SizedBox(height: 12),
LinkField(
onSaved: (value) => setState(() => _link = value ?? ''),
),
const SizedBox(height: 12),
AttendeesField(
onSaved: (value) => setState(() => _maxParticipants = value ?? 0),
),
const SizedBox(height: 12),
TagsField(
onSaved: (value) => setState(() => _tags = value ?? []),
),
const SizedBox(height: 12),
OrganizerField(
onSaved: (value) => setState(() => _organizer = value ?? ''),
),
const SizedBox(height: 12),
TransportInfoField(
onSaved: (value) => setState(() => _transportInfo = value ?? ''),
),
const SizedBox(height: 12),
AccommodationInfoField(
onSaved: (value) => setState(() => _accommodationInfo = value ?? ''),
),
const SizedBox(height: 12),
PrivacyRulesField(
onSaved: (value) => setState(() => _privacyRules = value ?? ''),
),
const SizedBox(height: 12),
SecurityProtocolField(
onSaved: (value) => setState(() => _securityProtocol = value ?? ''),
),
const SizedBox(height: 12),
ParkingField(
onSaved: (value) => setState(() => _hasParking = (value as bool?) ?? false),
),
const SizedBox(height: 12),
AccessibilityField(
onSaved: (value) => setState(() => _participationFee = (value as int?) ?? 0),
),
const SizedBox(height: 12),
ParticipationFeeField(
onSaved: (value) => setState(() => _participationFee = (value as int?) ?? 0),
),
const SizedBox(height: 12),
SubmitButton(
text: 'Créer l\'événement',
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
// Log des données de l'événement avant l'envoi
AppLogger.i(
'Création d\'événement: titre=$_title, lieu=$_location, catégorie=$_category, participants=$_maxParticipants',
tag: 'AddEventDialog',
);
AppLogger.d(
'Détails: description=${_description.length} chars, dates=$_selectedDate -> $_endDate, tags=${_tags.length}, image=${_selectedImageFile != null}',
tag: 'AddEventDialog',
);
// Logique d'envoi des données vers le backend...
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Événement créé avec succès !')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Veuillez remplir tous les champs requis')),
);
}
},
),
],
),
),
),
),
],
),
);
}
// En-tête de section pour mieux organiser les champs
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
);
}
}