558 lines
20 KiB
Dart
558 lines
20 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:geosector_app/core/theme/app_theme.dart';
|
|
import 'package:geosector_app/presentation/widgets/chat/chat_sidebar.dart';
|
|
import 'package:geosector_app/presentation/widgets/chat/chat_messages.dart';
|
|
import 'package:geosector_app/presentation/widgets/chat/chat_input.dart';
|
|
|
|
class AdminCommunicationPage extends StatefulWidget {
|
|
const AdminCommunicationPage({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<AdminCommunicationPage> createState() => _AdminCommunicationPageState();
|
|
}
|
|
|
|
class _AdminCommunicationPageState extends State<AdminCommunicationPage> {
|
|
int selectedContactId = 0;
|
|
String selectedContactName = '';
|
|
bool isTeamChat = true;
|
|
String messageText = '';
|
|
bool isReplying = false;
|
|
Map<String, dynamic>? replyingTo;
|
|
|
|
// Données simulées pour les conversations d'équipe
|
|
final List<Map<String, dynamic>> teamContacts = [
|
|
{
|
|
'id': 1,
|
|
'name': 'Équipe',
|
|
'isGroup': true,
|
|
'lastMessage': 'Réunion à 14h aujourd\'hui',
|
|
'time': DateTime.now().subtract(const Duration(minutes: 30)),
|
|
'unread': 2,
|
|
'online': true,
|
|
'avatar': 'assets/images/team.png',
|
|
},
|
|
{
|
|
'id': 2,
|
|
'name': 'Jean Dupont',
|
|
'isGroup': false,
|
|
'lastMessage': 'Je serai présent demain',
|
|
'time': DateTime.now().subtract(const Duration(hours: 1)),
|
|
'unread': 0,
|
|
'online': true,
|
|
'avatar': 'assets/images/avatar1.png',
|
|
},
|
|
{
|
|
'id': 3,
|
|
'name': 'Marie Martin',
|
|
'isGroup': false,
|
|
'lastMessage': 'Secteur Sud terminé',
|
|
'time': DateTime.now().subtract(const Duration(hours: 3)),
|
|
'unread': 1,
|
|
'online': false,
|
|
'avatar': 'assets/images/avatar2.png',
|
|
},
|
|
{
|
|
'id': 4,
|
|
'name': 'Pierre Legrand',
|
|
'isGroup': false,
|
|
'lastMessage': 'J\'ai une question sur mon secteur',
|
|
'time': DateTime.now().subtract(const Duration(days: 1)),
|
|
'unread': 0,
|
|
'online': false,
|
|
'avatar': 'assets/images/avatar3.png',
|
|
},
|
|
];
|
|
|
|
// Données simulées pour les conversations clients
|
|
final List<Map<String, dynamic>> clientContacts = [
|
|
{
|
|
'id': 101,
|
|
'name': 'Martin Durand',
|
|
'isGroup': false,
|
|
'lastMessage': 'Merci pour votre passage',
|
|
'time': DateTime.now().subtract(const Duration(hours: 5)),
|
|
'unread': 0,
|
|
'online': false,
|
|
'avatar': null,
|
|
'email': 'martin.durand@example.com',
|
|
},
|
|
{
|
|
'id': 102,
|
|
'name': 'Sophie Lambert',
|
|
'isGroup': false,
|
|
'lastMessage': 'Question concernant le reçu',
|
|
'time': DateTime.now().subtract(const Duration(days: 1)),
|
|
'unread': 3,
|
|
'online': false,
|
|
'avatar': null,
|
|
'email': 'sophie.lambert@example.com',
|
|
},
|
|
{
|
|
'id': 103,
|
|
'name': 'Thomas Bernard',
|
|
'isGroup': false,
|
|
'lastMessage': 'Rendez-vous manqué',
|
|
'time': DateTime.now().subtract(const Duration(days: 2)),
|
|
'unread': 0,
|
|
'online': false,
|
|
'avatar': null,
|
|
'email': 'thomas.bernard@example.com',
|
|
},
|
|
];
|
|
|
|
// Messages simulés pour la conversation sélectionnée
|
|
final Map<int, List<Map<String, dynamic>>> chatMessages = {
|
|
1: [
|
|
{
|
|
'id': 1,
|
|
'senderId': 2,
|
|
'senderName': 'Jean Dupont',
|
|
'message':
|
|
'Bonjour à tous, comment avance la collecte dans vos secteurs ?',
|
|
'time': DateTime.now().subtract(const Duration(days: 1, hours: 3)),
|
|
'isRead': true,
|
|
'avatar': 'assets/images/avatar1.png',
|
|
},
|
|
{
|
|
'id': 2,
|
|
'senderId': 3,
|
|
'senderName': 'Marie Martin',
|
|
'message': 'J\'ai terminé le secteur Sud avec 45 passages réalisés !',
|
|
'time': DateTime.now()
|
|
.subtract(const Duration(days: 1, hours: 2, minutes: 30)),
|
|
'isRead': true,
|
|
'avatar': 'assets/images/avatar2.png',
|
|
},
|
|
{
|
|
'id': 3,
|
|
'senderId': 4,
|
|
'senderName': 'Pierre Legrand',
|
|
'message':
|
|
'Secteur Est en cours, j\'ai réalisé 28 passages pour l\'instant.',
|
|
'time': DateTime.now().subtract(const Duration(days: 1, hours: 2)),
|
|
'isRead': true,
|
|
'avatar': 'assets/images/avatar3.png',
|
|
},
|
|
{
|
|
'id': 4,
|
|
'senderId': 0,
|
|
'senderName': 'Vous',
|
|
'message':
|
|
'Parfait, n\'oubliez pas la réunion de demain à 14h pour faire le point !',
|
|
'time': DateTime.now().subtract(const Duration(hours: 1)),
|
|
'isRead': true,
|
|
},
|
|
{
|
|
'id': 5,
|
|
'senderId': 2,
|
|
'senderName': 'Jean Dupont',
|
|
'message': 'Je serai présent 👍',
|
|
'time': DateTime.now().subtract(const Duration(minutes: 30)),
|
|
'isRead': false,
|
|
'avatar': 'assets/images/avatar1.png',
|
|
},
|
|
],
|
|
2: [
|
|
{
|
|
'id': 101,
|
|
'senderId': 2,
|
|
'senderName': 'Jean Dupont',
|
|
'message':
|
|
'Bonjour, est-ce que je peux commencer le secteur Ouest demain ?',
|
|
'time': DateTime.now().subtract(const Duration(days: 2)),
|
|
'isRead': true,
|
|
'avatar': 'assets/images/avatar1.png',
|
|
},
|
|
{
|
|
'id': 102,
|
|
'senderId': 0,
|
|
'senderName': 'Vous',
|
|
'message': 'Bonjour Jean, oui bien sûr. Les documents sont prêts.',
|
|
'time': DateTime.now()
|
|
.subtract(const Duration(days: 2))
|
|
.add(const Duration(minutes: 15)),
|
|
'isRead': true,
|
|
},
|
|
{
|
|
'id': 103,
|
|
'senderId': 2,
|
|
'senderName': 'Jean Dupont',
|
|
'message': 'Merci ! Je passerai les récupérer ce soir.',
|
|
'time': DateTime.now()
|
|
.subtract(const Duration(days: 2))
|
|
.add(const Duration(minutes: 20)),
|
|
'isRead': true,
|
|
'avatar': 'assets/images/avatar1.png',
|
|
},
|
|
{
|
|
'id': 104,
|
|
'senderId': 2,
|
|
'senderName': 'Jean Dupont',
|
|
'message': 'Je serai présent à la réunion de demain.',
|
|
'time': DateTime.now().subtract(const Duration(hours: 1)),
|
|
'isRead': true,
|
|
'avatar': 'assets/images/avatar1.png',
|
|
},
|
|
],
|
|
101: [
|
|
{
|
|
'id': 201,
|
|
'senderId': 101,
|
|
'senderName': 'Martin Durand',
|
|
'message':
|
|
'Bonjour, je voulais vous remercier pour votre passage. J\'ai bien reçu le reçu par email.',
|
|
'time': DateTime.now().subtract(const Duration(days: 1, hours: 5)),
|
|
'isRead': true,
|
|
},
|
|
{
|
|
'id': 202,
|
|
'senderId': 0,
|
|
'senderName': 'Vous',
|
|
'message':
|
|
'Bonjour M. Durand, je vous remercie pour votre contribution. N\'hésitez pas si vous avez des questions.',
|
|
'time': DateTime.now().subtract(const Duration(days: 1, hours: 4)),
|
|
'isRead': true,
|
|
},
|
|
{
|
|
'id': 203,
|
|
'senderId': 101,
|
|
'senderName': 'Martin Durand',
|
|
'message': 'Tout est parfait, merci !',
|
|
'time': DateTime.now().subtract(const Duration(hours: 5)),
|
|
'isRead': true,
|
|
},
|
|
],
|
|
102: [
|
|
{
|
|
'id': 301,
|
|
'senderId': 102,
|
|
'senderName': 'Sophie Lambert',
|
|
'message':
|
|
'Bonjour, je n\'ai pas reçu le reçu suite à mon paiement d\'hier. Pouvez-vous vérifier ?',
|
|
'time': DateTime.now().subtract(const Duration(days: 1, hours: 3)),
|
|
'isRead': true,
|
|
},
|
|
{
|
|
'id': 302,
|
|
'senderId': 0,
|
|
'senderName': 'Vous',
|
|
'message':
|
|
'Bonjour Mme Lambert, je m\'excuse pour ce désagrément. Je vérifie cela immédiatement.',
|
|
'time': DateTime.now().subtract(const Duration(days: 1, hours: 2)),
|
|
'isRead': true,
|
|
},
|
|
{
|
|
'id': 303,
|
|
'senderId': 0,
|
|
'senderName': 'Vous',
|
|
'message':
|
|
'Il semble qu\'il y ait eu un problème technique. Je viens de renvoyer le reçu à votre adresse email. Pourriez-vous vérifier si vous l\'avez bien reçu ?',
|
|
'time': DateTime.now().subtract(const Duration(days: 1, hours: 1)),
|
|
'isRead': true,
|
|
},
|
|
{
|
|
'id': 304,
|
|
'senderId': 102,
|
|
'senderName': 'Sophie Lambert',
|
|
'message':
|
|
'Je n\'ai toujours rien reçu. Mon email est-il correct ? C\'est sophie.lambert@example.com',
|
|
'time': DateTime.now().subtract(const Duration(days: 1)),
|
|
'isRead': true,
|
|
},
|
|
{
|
|
'id': 305,
|
|
'senderId': 102,
|
|
'senderName': 'Sophie Lambert',
|
|
'message': 'Est-ce que vous pouvez réessayer ?',
|
|
'time': DateTime.now().subtract(const Duration(hours: 5)),
|
|
'isRead': false,
|
|
},
|
|
{
|
|
'id': 306,
|
|
'senderId': 102,
|
|
'senderName': 'Sophie Lambert',
|
|
'message': 'Toujours pas de nouvelles...',
|
|
'time': DateTime.now().subtract(const Duration(hours: 3)),
|
|
'isRead': false,
|
|
},
|
|
{
|
|
'id': 307,
|
|
'senderId': 102,
|
|
'senderName': 'Sophie Lambert',
|
|
'message': 'Pouvez-vous me contacter dès que possible ?',
|
|
'time': DateTime.now().subtract(const Duration(hours: 1)),
|
|
'isRead': false,
|
|
},
|
|
],
|
|
};
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final screenWidth = MediaQuery.of(context).size.width;
|
|
final isDesktop = screenWidth > 800;
|
|
|
|
return Row(
|
|
children: [
|
|
// Sidebar des contacts (fixe sur desktop, conditional sur mobile)
|
|
if (isDesktop || selectedContactId == 0)
|
|
SizedBox(
|
|
width: isDesktop ? 320 : screenWidth,
|
|
child: ChatSidebar(
|
|
teamContacts: teamContacts,
|
|
clientContacts: clientContacts,
|
|
isTeamChat: isTeamChat,
|
|
selectedContactId: selectedContactId,
|
|
onContactSelected: (contactId, contactName, isTeam) {
|
|
setState(() {
|
|
selectedContactId = contactId;
|
|
selectedContactName = contactName;
|
|
isTeamChat = isTeam;
|
|
replyingTo = null;
|
|
isReplying = false;
|
|
});
|
|
},
|
|
onToggleGroup: (isTeam) {
|
|
setState(() {
|
|
isTeamChat = isTeam;
|
|
selectedContactId = 0;
|
|
selectedContactName = '';
|
|
});
|
|
},
|
|
),
|
|
),
|
|
|
|
// Vue des messages (conditionnelle sur mobile)
|
|
if (isDesktop || selectedContactId != 0)
|
|
Expanded(
|
|
child: selectedContactId == 0
|
|
? const Center(
|
|
child: Text('Sélectionnez une conversation pour commencer'),
|
|
)
|
|
: Column(
|
|
children: [
|
|
// En-tête de la conversation
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: AppTheme.spacingL,
|
|
vertical: AppTheme.spacingM,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 5,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
if (!isDesktop)
|
|
IconButton(
|
|
icon: const Icon(Icons.arrow_back),
|
|
onPressed: () {
|
|
setState(() {
|
|
selectedContactId = 0;
|
|
selectedContactName = '';
|
|
});
|
|
},
|
|
),
|
|
CircleAvatar(
|
|
radius: 20,
|
|
backgroundColor:
|
|
AppTheme.primaryColor.withOpacity(0.2),
|
|
backgroundImage:
|
|
_getAvatarForContact(selectedContactId),
|
|
child: _getAvatarForContact(selectedContactId) ==
|
|
null
|
|
? Text(
|
|
selectedContactName.isNotEmpty
|
|
? selectedContactName[0].toUpperCase()
|
|
: '',
|
|
style: const TextStyle(
|
|
color: AppTheme.primaryColor,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
const SizedBox(width: AppTheme.spacingM),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
selectedContactName,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
if (!isTeamChat && selectedContactId > 100)
|
|
Text(
|
|
_getEmailForContact(selectedContactId),
|
|
style: TextStyle(
|
|
color: Colors.grey[600],
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.info_outline),
|
|
onPressed: () {
|
|
// Afficher les détails du contact
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// Messages
|
|
Expanded(
|
|
child: ChatMessages(
|
|
messages: chatMessages[selectedContactId] ?? [],
|
|
currentUserId: 0,
|
|
onReply: (message) {
|
|
setState(() {
|
|
isReplying = true;
|
|
replyingTo = message;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
|
|
// Zone de réponse
|
|
if (isReplying)
|
|
Container(
|
|
padding: const EdgeInsets.all(AppTheme.spacingM),
|
|
color: Colors.grey[100],
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 4,
|
|
height: 40,
|
|
decoration: BoxDecoration(
|
|
color: AppTheme.primaryColor,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
),
|
|
const SizedBox(width: AppTheme.spacingM),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Réponse à ${replyingTo?['senderName']}',
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 12,
|
|
color: AppTheme.primaryColor,
|
|
),
|
|
),
|
|
Text(
|
|
replyingTo?['message'] ?? '',
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.close),
|
|
onPressed: () {
|
|
setState(() {
|
|
isReplying = false;
|
|
replyingTo = null;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// Zone de saisie du message
|
|
ChatInput(
|
|
onMessageSent: (text) {
|
|
setState(() {
|
|
// Ajouter le message à la conversation
|
|
if (chatMessages[selectedContactId] != null) {
|
|
final newMessageId =
|
|
chatMessages[selectedContactId]!.last['id'] +
|
|
1;
|
|
|
|
chatMessages[selectedContactId]!.add({
|
|
'id': newMessageId,
|
|
'senderId': 0,
|
|
'senderName': 'Vous',
|
|
'message': text,
|
|
'time': DateTime.now(),
|
|
'isRead': false,
|
|
'replyTo': isReplying ? replyingTo : null,
|
|
});
|
|
|
|
// Mise à jour du dernier message pour le contact
|
|
final contactsList =
|
|
isTeamChat ? teamContacts : clientContacts;
|
|
final contactIndex = contactsList.indexWhere(
|
|
(c) => c['id'] == selectedContactId);
|
|
|
|
if (contactIndex != -1) {
|
|
contactsList[contactIndex]['lastMessage'] =
|
|
text;
|
|
contactsList[contactIndex]['time'] =
|
|
DateTime.now();
|
|
contactsList[contactIndex]['unread'] = 0;
|
|
}
|
|
|
|
isReplying = false;
|
|
replyingTo = null;
|
|
}
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
ImageProvider? _getAvatarForContact(int contactId) {
|
|
String? avatarPath;
|
|
|
|
if (isTeamChat) {
|
|
final contact = teamContacts.firstWhere(
|
|
(c) => c['id'] == contactId,
|
|
orElse: () => {'avatar': null},
|
|
);
|
|
avatarPath = contact['avatar'];
|
|
} else {
|
|
final contact = clientContacts.firstWhere(
|
|
(c) => c['id'] == contactId,
|
|
orElse: () => {'avatar': null},
|
|
);
|
|
avatarPath = contact['avatar'];
|
|
}
|
|
|
|
return avatarPath != null ? AssetImage(avatarPath) : null;
|
|
}
|
|
|
|
String _getEmailForContact(int contactId) {
|
|
if (!isTeamChat) {
|
|
final contact = clientContacts.firstWhere(
|
|
(c) => c['id'] == contactId,
|
|
orElse: () => {'email': ''},
|
|
);
|
|
return contact['email'] ?? '';
|
|
}
|
|
return '';
|
|
}
|
|
}
|