declare global { interface Window { toggleUserList: () => void; switchUser: (userId: string | number) => void; loadMemberChat: (memberId: string | number) => void; } } import { groupsMock } from '../../mocks/mock-signature/groupsMock'; import { messagesMock as initialMessagesMock, messagesMock } from '../../mocks/mock-signature/messagesMock'; import { membersMock } from '../../mocks/mock-signature/membersMocks'; import { Message, DocumentSignature, } from '../../models/signature.models'; import { messageStore } from '../../utils/messageMock'; import { Member } from '../../interface/memberInterface'; import { Group } from '../../interface/groupInterface'; import { getCorrectDOM } from '../../utils/document.utils'; import chatStyle from '../../../public/style/chat.css?inline'; let currentUser: Member = membersMock[0]; interface LocalNotification { memberId: string; text: string; time: string; } export function initChat() { const chatElement = document.createElement('chat-element'); const container = document.querySelector('.container'); if (container) { container.appendChild(chatElement); } } class ChatElement extends HTMLElement { static get observedAttributes() { return ['process-id']; } private processId: string | null = null; private selectedMemberId: string | null = null; private messagesMock: any[] = []; private dom: Node; private notifications: LocalNotification[] = []; private notificationBadge = document.querySelector('.notification-badge'); private notificationBoard = document.getElementById('notification-board'); private notificationBell = document.getElementById('notification-bell'); private selectedSignatories: DocumentSignature[] = []; private allMembers = membersMock.map(member => ({ id: member.id, name: member.name, roleName: 'Default Role' })); constructor() { super(); this.attachShadow({ mode: 'open' }); this.messagesMock = messageStore.getMessages(); this.dom = getCorrectDOM('signature-element'); // Récupérer le processId depuis l'attribut du composant this.processId = this.getAttribute('process-id'); console.log('🔍 Constructor - Process ID from element:', this.processId); this.shadowRoot!.innerHTML = `
`; window.toggleUserList = this.toggleUserList.bind(this); window.switchUser = this.switchUser.bind(this); window.loadMemberChat = this.loadMemberChat.bind(this); // Initialiser les événements de notification document.addEventListener('click', (event: Event): void => { if (this.notificationBoard && this.notificationBoard.style.display === 'block' && !this.notificationBoard.contains(event.target as Node) && this.notificationBell && !this.notificationBell.contains(event.target as Node)) { this.notificationBoard.style.display = 'none'; } }); this.initMessageEvents(); this.initFileUpload(); document.addEventListener('newMessagingProcess', ((event: CustomEvent) => { console.log('🎯 Received newMessagingProcess event:', event.detail); this.addNewMessagingProcess(event.detail.processId, event.detail.processName); }) as EventListener); } attributeChangedCallback(name: string, oldValue: string, newValue: string) { console.log(`🔄 Attribute ${name} changed from ${oldValue} to ${newValue}`); if (name === 'process-id' && newValue) { console.log('🔍 Loading chat with new process ID:', newValue); this.loadGroupList(newValue); } } private initMessageEvents() { // Pour le bouton Send const sendButton = this.shadowRoot?.querySelector('#send-button'); if (sendButton) { sendButton.addEventListener('click', () => this.sendMessage()); } // Pour la touche Entrée const messageInput = this.shadowRoot?.querySelector('#message-input'); if (messageInput) { messageInput.addEventListener('keypress', (event: Event) => { const keyEvent = event as KeyboardEvent; // Cast en KeyboardEvent if (keyEvent.key === 'Enter' && !keyEvent.shiftKey) { event.preventDefault(); this.sendMessage(); } }); } } private initFileUpload() { const fileInput = this.shadowRoot?.querySelector('#file-input') as HTMLInputElement; if (fileInput) { fileInput.addEventListener('change', (event: Event) => { const target = event.target as HTMLInputElement; if (target.files && target.files.length > 0) { this.sendFile(target.files[0]); } }); } } ///////////////////// Notification module ///////////////////// // Delete a notification private removeNotification(index: number) { this.notifications?.splice(index, 1); // Ajout de ?. this.renderNotifications(); this.updateNotificationBadge(); } // Show notifications private renderNotifications() { if (!this.notificationBoard) return; // Reset the interface this.notificationBoard.innerHTML = ''; // Displays "No notifications available" if there are no notifications if (this.notifications.length === 0) { this.notificationBoard.innerHTML = '
No notifications available
'; return; } // Add each notification to the list this.notifications.forEach((notif, index) => { const notifElement = document.createElement('div'); notifElement.className = 'notification-item'; notifElement.textContent = `${notif.text} at ${notif.time}`; notifElement.onclick = () => { this.loadMemberChat(notif.memberId); this.removeNotification(index); }; this.notificationBoard?.appendChild(notifElement); }); } private updateNotificationBadge() { if (!this.notificationBadge) return; const count = this.notifications.length; this.notificationBadge.textContent = count > 99 ? '+99' : count.toString(); (this.notificationBadge as HTMLElement).style.display = count > 0 ? 'block' : 'none'; } // Add notification private addNotification(memberId: string, message: Message) { // Creating a new notification const notification = { memberId, text: `New message from Member ${memberId}: ${message.text}`, time: message.time }; // Added notification to list and interface this.notifications.push(notification); this.renderNotifications(); this.updateNotificationBadge(); } // Send a messsage private sendMessage() { const messageInput = this.shadowRoot?.querySelector('#message-input') as HTMLInputElement; if (!messageInput) return; const messageText = messageInput.value.trim(); if (messageText === '' || this.selectedMemberId === null) { return; } const newMessage: Message = { id: Date.now(), sender: "4NK", text: messageText, time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), type: 'text' as const }; // Add and display the message immediately messageStore.addMessage(this.selectedMemberId, newMessage); this.messagesMock = messageStore.getMessages(); this.loadMemberChat(this.selectedMemberId); // Reset the input messageInput.value = ''; // Automatic response after 2 seconds setTimeout(() => { if (this.selectedMemberId) { const autoReply = this.generateAutoReply(`Member ${this.selectedMemberId}`); messageStore.addMessage(this.selectedMemberId, autoReply); this.messagesMock = messageStore.getMessages(); this.loadMemberChat(this.selectedMemberId); this.addNotification(this.selectedMemberId, autoReply); } }, 2000); } // Scroll down the conversation after loading messages private scrollToBottom(container: Element) { (container as HTMLElement).scrollTop = (container as HTMLElement).scrollHeight; } // Load the list of members private loadMemberChat(memberId: string | number) { this.selectedMemberId = String(memberId); const memberMessages = this.messagesMock.find(m => String(m.memberId) === String(memberId)); // Find the process and the role of the member let memberInfo = { processName: '', roleName: '', memberName: '' }; groupsMock.forEach(process => { process.roles.forEach(role => { const member = role.members.find(m => String(m.id) === String(memberId)); if (member) { memberInfo = { processName: process.name, roleName: role.name, memberName: member.name }; } }); }); const chatHeader = this.shadowRoot?.querySelector('#chat-header'); const messagesContainer = this.shadowRoot?.querySelector('#messages'); if (!chatHeader || !messagesContainer) return; chatHeader.textContent = `Chat with ${memberInfo.roleName} ${memberInfo.memberName} from ${memberInfo.processName}`; messagesContainer.innerHTML = ''; if (memberMessages) { memberMessages.messages.forEach((message: Message) => { const messageElement = document.createElement('div'); messageElement.className = 'message-container'; const messageContent = document.createElement('div'); messageContent.className = 'message'; if (message.type === 'file') { messageContent.innerHTML = `${message.fileName}`; messageContent.classList.add('user'); } else { messageContent.innerHTML = `${message.sender}: ${message.text} ${message.time}`; if (message.sender === "4NK") { messageContent.classList.add('user'); } } messageElement.appendChild(messageContent); messagesContainer.appendChild(messageElement); }); } this.scrollToBottom(messagesContainer); } private toggleMembers(role: { members: { id: string | number; name: string; }[] }, roleElement: HTMLElement) { let memberList = roleElement.querySelector('.member-list'); if (memberList) { (memberList as HTMLElement).style.display = (memberList as HTMLElement).style.display === 'none' ? 'block' : 'none'; return; } memberList = document.createElement('ul'); memberList.className = 'member-list'; role.members.forEach(member => { const memberItem = document.createElement('li'); memberItem.textContent = member.name; memberItem.onclick = (event) => { event.stopPropagation(); this.loadMemberChat(member.id.toString()); }; memberList.appendChild(memberItem); }); roleElement.appendChild(memberList); } // Toggle the list of Roles private toggleRoles(group: Group, groupElement: HTMLElement) { console.log('=== toggleRoles START ==='); console.log('Group:', group.name); console.log('Group roles:', group.roles); // Afficher tous les rôles disponibles let roleList = groupElement.querySelector('.role-list'); console.log('Existing roleList:', roleList); if (roleList) { const roleItems = roleList.querySelectorAll('.role-item'); roleItems.forEach(roleItem => { console.log('Processing roleItem:', roleItem.innerHTML); // Voir le contenu HTML complet let container = roleItem.querySelector('.role-item-container'); if (!container) { container = document.createElement('div'); container.className = 'role-item-container'; // Créer un span pour le nom du rôle const nameSpan = document.createElement('span'); nameSpan.className = 'role-name'; nameSpan.textContent = roleItem.textContent?.trim() || ''; container.appendChild(nameSpan); roleItem.textContent = ''; roleItem.appendChild(container); } // Récupérer le nom du rôle const roleName = roleItem.textContent?.trim(); console.log('Role name from textContent:', roleName); // Alternative pour obtenir le nom du rôle const roleNameAlt = container.querySelector('.role-name')?.textContent; console.log('Role name from span:', roleNameAlt); if (!container.querySelector('.folder-icon')) { const folderButton = document.createElement('span'); folderButton.className = 'folder-icon'; folderButton.addEventListener('click', (event) => { event.stopPropagation(); console.log('Clicked role name:', roleName); console.log('Available roles:', group.roles.map(r => r.name)); const role = group.roles.find(r => r.name === roleName); if (role) { console.log('Found role:', role); } else { console.error('Role not found. Name:', roleName); console.error('Available roles:', group.roles); } }); container.appendChild(folderButton); } }); (roleList as HTMLElement).style.display = (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none'; } } private loadGroupList(processId: string): void { console.log('🔍 Loading group list with processId:', processId); const groupList = this.shadowRoot?.querySelector('#group-list'); if (!groupList) { console.error('❌ Group list element not found'); return; } // Vider la liste existante groupList.innerHTML = ''; // Créer l'élément pour le processus de messaging const li = document.createElement('li'); li.className = 'group-list-item'; li.setAttribute('data-process-id', processId); const container = document.createElement('div'); container.className = 'group-item-container'; const nameSpan = document.createElement('span'); nameSpan.textContent = `Messaging Process ${processId}`; nameSpan.className = 'process-name'; container.appendChild(nameSpan); li.appendChild(container); groupList.appendChild(li); console.log('✅ Group list item added for process:', processId); } // Function to manage the list of users private toggleUserList() { const userList = getCorrectDOM('userList'); if (!userList) return; if (!(userList as HTMLElement).classList.contains('show')) { (userList as HTMLElement).innerHTML = membersMock.map(member => `
${member.avatar}
${member.name} ${member.email}
`).join(''); } (userList as HTMLElement).classList.toggle('show'); } private switchUser(userId: string | number) { const user = membersMock.find(member => member.id === userId); if (!user) return; currentUser = user; this.updateCurrentUserDisplay(); const userList = getCorrectDOM('userList') as HTMLElement; userList?.classList.remove('show'); } // Function to update the display of the current user private updateCurrentUserDisplay() { const userDisplay = getCorrectDOM('current-user') as HTMLElement; if (userDisplay) { userDisplay.innerHTML = `
${currentUser.avatar} ${currentUser.name}
`; } } // Generate an automatic response private generateAutoReply(senderName: string): Message { return { id: Date.now(), sender: senderName, text: "OK...", time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), type: 'text' as const }; } // Send a file private sendFile(file: File) { console.log('SendFile called with file:', file); const reader = new FileReader(); reader.onloadend = () => { const fileData = reader.result; const fileName = file.name; console.log('File loaded:', fileName); if (this.selectedMemberId) { messageStore.addMessage(this.selectedMemberId, { id: Date.now(), sender: "4NK", fileName: fileName, fileData: fileData, time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), type: 'file' }); console.log('Message added to store'); this.messagesMock = messageStore.getMessages(); this.loadMemberChat(this.selectedMemberId); } }; reader.readAsDataURL(file); } private initializeEventListeners() { document.addEventListener('DOMContentLoaded', (): void => { }); // Gestionnaire d'événements pour le chat const sendBtn = this.shadowRoot?.querySelector('#send-button'); if (sendBtn) { sendBtn.addEventListener('click', this.sendMessage.bind(this)); } const messageInput = this.shadowRoot?.querySelector('#message-input'); if (messageInput) { messageInput.addEventListener('keypress', (event: Event) => { if ((event as KeyboardEvent).key === 'Enter') { event.preventDefault(); this.sendMessage(); } }); } // Gestionnaire pour l'envoi de fichiers const fileInput = this.shadowRoot?.querySelector('#file-input'); if (fileInput) { fileInput.addEventListener('change', (event: Event) => { const file = (event.target as HTMLInputElement).files?.[0]; if (file) { this.sendFile(file); } }); } } connectedCallback() { this.updateCurrentUserDisplay(); this.initializeEventListeners(); if (this.processId) { console.log('🔍 Loading chat with process ID:', this.processId); this.loadGroupList(this.processId); } else { console.error('❌ No process ID found in element attributes'); } // Si un membre est sélectionné par défaut, charger ses messages if (this.selectedMemberId) { this.loadMemberChat(this.selectedMemberId); } } private addNewMessagingProcess(processId: string, processName: string) { console.log('🎯 Adding new messaging process:', { processId, processName }); const groupList = this.shadowRoot?.querySelector('#group-list'); if (!groupList) { console.error('Group list not found in shadow DOM'); return; } // Vérifier si le processus existe déjà const existingProcess = groupList.querySelector(`[data-process-id="${processId}"]`); if (existingProcess) { console.log('Process already exists:', processId); return; } // Créer le nouveau groupe const li = document.createElement('li'); li.className = 'group-list-item'; li.setAttribute('data-process-id', processId); // Créer le conteneur flex const container = document.createElement('div'); container.className = 'group-item-container'; // Span pour le nom du processus const nameSpan = document.createElement('span'); nameSpan.textContent = processName; nameSpan.className = 'process-name'; // Assembler les éléments container.appendChild(nameSpan); li.appendChild(container); // Créer la liste des rôles const roleList = document.createElement('ul'); roleList.className = 'role-list'; roleList.style.display = 'none'; // Ajouter un rôle par défaut pour le messaging const roleItem = document.createElement('li'); roleItem.className = 'role-item'; roleItem.textContent = 'Messaging'; roleList.appendChild(roleItem); li.appendChild(roleList); groupList.appendChild(li); console.log('🎯 New messaging process added successfully'); } } customElements.define('chat-element', ChatElement); export { ChatElement };