component_ok

This commit is contained in:
Pascal 2024-12-18 13:57:49 +01:00 committed by Sosthene
parent 5e1b22d302
commit 6652354b33
11 changed files with 3897 additions and 3772 deletions

View File

@ -595,10 +595,11 @@ body {
.container { .container {
display: flex; display: flex;
flex: 1; flex: 1;
height: calc(100% - 4vh); height: 90vh;
margin-top: 4vh; margin-top: 9vh;
margin-left: -1%; margin-left: -1%;
text-align: left; text-align: left;
width: 209vh;
} }
/* Liste des information sur l'account */ /* Liste des information sur l'account */
@ -612,6 +613,8 @@ body {
overflow-y: auto; overflow-y: auto;
border-right: 2px solid #2c3e50; border-right: 2px solid #2c3e50;
flex-shrink: 0; flex-shrink: 0;
padding-right: 10px;
height: 91vh;
} }
.parameter-list ul { .parameter-list ul {
@ -640,9 +643,10 @@ body {
background-color: #ffffff; background-color: #ffffff;
border-radius: 10px; border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
margin: 10px; margin: 0px;
margin-top: 20px; margin-top: 20px;
margin-left: 0%; margin-left: 1%;
margin-bottom: -7px;
} }
/* En-tête du parametre */ /* En-tête du parametre */
@ -1317,3 +1321,46 @@ body {
.banner-image.clickable:hover { .banner-image.clickable:hover {
opacity: 0.8; opacity: 0.8;
} }
.parameter-list-ul.profile {
position: relative;
overflow: hidden;
max-height: 200px;
margin-bottom: 20px;
}
.profile-preview {
position: relative;
width: 100%;
height: 100%;
}
.preview-banner {
position: relative;
width: 100%;
height: 120px;
overflow: hidden;
}
.preview-banner-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.preview-info {
position: relative;
display: flex;
align-items: center;
padding: 10px;
gap: 10px;
background: rgba(0, 0, 0, 0.3);
}
.preview-avatar {
width: 45px;
height: 45px;
border-radius: 50%;
border: 2px solid white;
}

View File

@ -145,10 +145,11 @@ body {
.container { .container {
display: flex; display: flex;
flex: 1; flex: 1;
height: calc(100% - 4vh); height: 90vh;
margin-top: 4vh; margin-top: 9vh;
margin-left: -1%; margin-left: -1%;
text-align: left; text-align: left;
width: 209vh;
} }
@ -164,8 +165,8 @@ body {
border-right: 2px solid #2c3e50; border-right: 2px solid #2c3e50;
flex-shrink: 0; flex-shrink: 0;
padding-right: 10px; padding-right: 10px;
height: 91vh;
} }
.group-list ul { .group-list ul {
cursor: pointer; cursor: pointer;
list-style: none; list-style: none;
@ -198,9 +199,10 @@ body {
background-color: #ffffff; background-color: #ffffff;
border-radius: 10px; border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
margin: 10px; margin: 0px;
margin-top: 20px; margin-top: 20px;
margin-left: 0%; margin-left: 1%;
margin-bottom: -7px;
} }
/* En-tête du chat */ /* En-tête du chat */

View File

@ -18,6 +18,8 @@ body {
flex-direction: column; flex-direction: column;
} }
/* 4NK NAVBAR */ /* 4NK NAVBAR */
.brand-logo { .brand-logo {
@ -144,10 +146,11 @@ body {
.container { .container {
display: flex; display: flex;
flex: 1; flex: 1;
height: calc(100% - 4vh); height: 90vh;
margin-top: 4vh; margin-top: 9vh;
margin-left: -1%; margin-left: -1%;
text-align: left; text-align: left;
width: 209vh;
} }
@ -163,6 +166,7 @@ body {
border-right: 2px solid #2c3e50; border-right: 2px solid #2c3e50;
flex-shrink: 0; flex-shrink: 0;
padding-right: 10px; padding-right: 10px;
height: 91vh;
} }
.group-list ul { .group-list ul {
@ -198,9 +202,10 @@ body {
background-color: #ffffff; background-color: #ffffff;
border-radius: 10px; border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
margin: 10px; margin: 0px;
margin-top: 20px; margin-top: 20px;
margin-left: 0%; margin-left: 1%;
margin-bottom: -7px;
} }
/* En-tête du chat */ /* En-tête du chat */

View File

@ -1,12 +0,0 @@
<div class="avatar-section">
<img src="https://via.placeholder.com/800x200" alt="Banner" class="banner-image" />
<div class="banner-content">
<div class="avatar-container">
<img src="https://via.placeholder.com/150" alt="Avatar" class="avatar" onclick="window.openAvatarPopup()" />
</div>
<div class="user-info">
<span class="user-name">John</span>
<span class="user-lastname">Doe</span>
</div>
</div>
</div>

View File

@ -1,99 +1,10 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Account</title> <title>Account</title>
<link rel="stylesheet" href="../../public/style/account.css" /> </head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" /> <body>
</head> <account-component></account-component>
<body> <script type="module" src="./account.ts"></script>
<!-- Header Container --> </body>
<div id="header-container"></div>
<!-- Profile Popup -->
<div id="avatar-popup" class="popup">
<div class="popup-content">
<span class="close-popup">&times;</span>
<h2>Profile</h2>
<!-- Banner Preview Section -->
<div class="banner-preview">
<div class="banner-image-container">
<img src="https://via.placeholder.com/800x200" alt="Banner" class="banner-image" id="popup-banner-img" />
<div class="banner-content">
<div class="avatar-container">
<img src="https://via.placeholder.com/150" alt="Avatar" class="avatar" id="popup-avatar-img" />
</div>
<div class="user-info">
<span class="editable" id="popup-name"></span>
<span class="editable" id="popup-lastname"></span>
</div>
</div>
</div>
<div class="banner-controls">
<label for="banner-upload" class="banner-upload-label button-style">
Change Banner Image
<input type="file" id="banner-upload" accept="image/*" style="display: none" />
</label>
</div>
</div>
<!-- Avatar Upload Section -->
<div class="popup-avatar">
<label for="avatar-upload" class="avatar-upload-label">
<img src="https://via.placeholder.com/150" alt="Avatar" class="avatar" id="popup-avatar-img" />
<div class="avatar-overlay">
<span>Change Avatar</span>
</div>
</label>
<input type="file" id="avatar-upload" accept="image/*" style="display: none" />
</div>
<!-- User Info Section -->
<div class="popup-info">
<p><strong>Name:</strong> <span class="editable" id="popup-name"></span></p>
<p><strong>Last Name:</strong> <span class="editable" id="popup-lastname"></span></p>
<p><strong>Address:</strong> 🏠 🌍 🗽🎊😩-🎊😑🎄😩</p>
</div>
<!-- Buttons Container -->
<div class="popup-buttons">
<button class="delete-account-btn" onclick="confirmDeleteAccount()">Delete Account</button>
</div>
</div>
</div>
<!-- Main Content -->
<div class="container">
<!-- Parameter List -->
<div class="parameter-list">
<ul class="parameter-list-ul" onclick="window.showPairing()">
Pairing 🔗
</ul>
<ul class="parameter-list-ul" onclick="window.showWallet()">
Wallet 👛
</ul>
<ul class="parameter-list-ul" onclick="window.showProcess()">
Process ⚙️
</ul>
<ul class="parameter-list-ul" onclick="window.showData()">
Data 💾
</ul>
</div>
<!-- Parameter Area -->
<div class="parameter-area">
<div class="content-container">
<div id="pairing-content"></div>
<div id="wallet-content"></div>
<div id="process-content"></div>
<div id="data-content"></div>
</div>
</div>
</div>
<!-- Scripts -->
<script type="module" src="./account.ts?ts"></script>
</body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +1,13 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Messagerie</title>
<link rel="stylesheet" href="../../public/style/chat.css" />
</head>
<body> <head>
<!-- Main content--> <title>Chat</title>
<div class="container"> </head>
<!-- List of groups -->
<div class="group-list">
<ul id="group-list">
<!-- Groups will be added here dynamically -->
</ul>
</div>
<!-- Chat area --> <body>
<div class="chat-area"> <chat-component></chat-component>
<div class="chat-header" id="chat-header"> <script type="module" src="./chat.ts"></script>
<!-- Chat title --> </body>
</div>
<div class="messages" id="messages">
<!-- Messages -->
</div>
<!-- Input area -->
<div class="input-area">
<label for="file-input" class="attachment-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path
d="M13.514 2.444l-10.815 10.785c-.449.449-.678 1.074-.625 1.707l.393 4.696c.041.479.422.86.9.9l4.697.394c.633.053 1.258-.177 1.707-.626l11.875-11.844c.196-.196.195-.512 0-.707l-3.536-3.536c-.195-.195-.511-.196-.707 0l-8.878 8.848c-.162.162-.253.382-.253.611v.725c0 .184.148.332.332.332h.725c.229 0 .448-.092.61-.254l7.11-7.08 1.415 1.415-7.386 7.354c-.375.375-.885.586-1.414.586h-2.414c-.555 0-1-.448-1-1v-2.414c0-.53.211-1.039.586-1.414l9.506-9.477c.781-.781 2.049-.781 2.829-.001l4.243 4.243c.391.391.586.902.586 1.414 0 .512-.196 1.025-.587 1.416l-12.35 12.319c-.748.747-1.76 1.164-2.81 1.164-.257 0-6.243-.467-6.499-.487-.664-.052-1.212-.574-1.268-1.267-.019-.242-.486-6.246-.486-6.499 0-1.05.416-2.062 1.164-2.811l10.936-10.936 1.414 1.444z"
/>
</svg>
</label>
<input type="file" id="file-input" style="display: none" />
<textarea id="message-input" rows="3" placeholder="Type your message..."></textarea>
<button id="send-button">Send</button>
</div>
</div>
</div>
<script type="module" src="./chat.ts?ts"></script>
</body>
</html> </html>

View File

@ -1,171 +1,218 @@
declare global { declare global {
interface Window { interface Window {
toggleUserList: () => void; toggleUserList: () => void;
switchUser: (userId: string | number) => void; switchUser: (userId: string | number) => void;
loadMemberChat: (memberId: string | number) => void; loadMemberChat: (memberId: string | number) => void;
} }
} }
import { groupsMock } from '../../mocks/mock-signature/groupsMock'; import { groupsMock } from '../../mocks/mock-signature/groupsMock';
import { messagesMock as initialMessagesMock, messagesMock } from '../../mocks/mock-signature/messagesMock'; import { messagesMock as initialMessagesMock, messagesMock } from '../../mocks/mock-signature/messagesMock';
import { membersMock } from '../../mocks/mock-signature/membersMocks'; import { membersMock } from '../../mocks/mock-signature/membersMocks';
import { Message, DocumentSignature } from '../../models/signature.models'; import {
Message,
DocumentSignature,
} from '../../models/signature.models';
import { messageStore } from '../../utils/messageMock'; import { messageStore } from '../../utils/messageMock';
import { Member } from '../../interface/memberInterface'; import { Member } from '../../interface/memberInterface';
import { Group } from '../../interface/groupInterface'; import { Group } from '../../interface/groupInterface';
import { getCorrectDOM } from '../../utils/document.utils'; import { getCorrectDOM } from '../../utils/document.utils';
import chatStyle from '../../../public/style/chat.css?inline';
let currentUser: Member = membersMock[0]; let currentUser: Member = membersMock[0];
interface LocalNotification { interface LocalNotification {
memberId: string; memberId: string;
text: string; text: string;
time: string; time: string;
} }
export function initChat() { export function initChat() {
const chatElement = document.createElement('chat-element'); const chatElement = document.createElement('chat-element');
const container = document.querySelector('.container'); const container = document.querySelector('.container');
if (container) { if (container) {
container.appendChild(chatElement); container.appendChild(chatElement);
} }
} }
class ChatElement extends HTMLElement { class ChatElement extends HTMLElement {
private selectedMemberId: string | null = null; private selectedMemberId: string | null = null;
private messagesMock: any[] = []; private messagesMock: any[] = [];
private dom: Node; private dom: Node;
private notifications: LocalNotification[] = []; private notifications: LocalNotification[] = [];
private notificationBadge = document.querySelector('.notification-badge'); private notificationBadge = document.querySelector('.notification-badge');
private notificationBoard = document.getElementById('notification-board'); private notificationBoard = document.getElementById('notification-board');
private notificationBell = document.getElementById('notification-bell'); private notificationBell = document.getElementById('notification-bell');
private selectedSignatories: DocumentSignature[] = []; private selectedSignatories: DocumentSignature[] = [];
private allMembers = membersMock.map((member) => ({ private allMembers = membersMock.map(member => ({
id: member.id, id: member.id,
name: member.name, name: member.name,
roleName: 'Default Role', roleName: 'Default Role'
})); }));
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.dom = getCorrectDOM('signature-element');
window.toggleUserList = this.toggleUserList.bind(this); constructor() {
window.switchUser = this.switchUser.bind(this); super();
window.loadMemberChat = this.loadMemberChat.bind(this); this.attachShadow({ mode: 'open' });
this.messagesMock = messageStore.getMessages();
this.dom = getCorrectDOM('signature-element');
// Initialiser les événements de notification this.shadowRoot!.innerHTML = `
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();
}
private initMessageEvents() { <style>
// Pour le bouton Send ${chatStyle}
const sendButton = document.getElementById('send-button'); </style>
if (sendButton) { <div class="container">
sendButton.addEventListener('click', () => this.sendMessage()); <!-- List of groups -->
<div class="group-list">
<ul id="group-list">
</ul>
</div>
<!-- Chat area -->
<div class="chat-area">
<div class="chat-header" id="chat-header">
<!-- Chat title -->
</div>
<div class="messages" id="messages">
<!-- Messages -->
</div>
<!-- Input area -->
<div class="input-area">
<label for="file-input" class="attachment-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M13.514 2.444l-10.815 10.785c-.449.449-.678 1.074-.625 1.707l.393 4.696c.041.479.422.86.9.9l4.697.394c.633.053 1.258-.177 1.707-.626l11.875-11.844c.196-.196.195-.512 0-.707l-3.536-3.536c-.195-.195-.511-.196-.707 0l-8.878 8.848c-.162.162-.253.382-.253.611v.725c0 .184.148.332.332.332h.725c.229 0 .448-.092.61-.254l7.11-7.08 1.415 1.415-7.386 7.354c-.375.375-.885.586-1.414.586h-2.414c-.555 0-1-.448-1-1v-2.414c0-.53.211-1.039.586-1.414l9.506-9.477c.781-.781 2.049-.781 2.829-.001l4.243 4.243c.391.391.586.902.586 1.414 0 .512-.196 1.025-.587 1.416l-12.35 12.319c-.748.747-1.76 1.164-2.81 1.164-.257 0-6.243-.467-6.499-.487-.664-.052-1.212-.574-1.268-1.267-.019-.242-.486-6.246-.486-6.499 0-1.05.416-2.062 1.164-2.811l10.936-10.936 1.414 1.444z"/>
</svg>
</label>
<input type="file" id="file-input" style="display: none;" />
<textarea id="message-input" rows="3" placeholder="Type your message..."></textarea>
<button id="send-button">Send</button>
</div>
</div>
</div>
`;
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();
} }
// Pour la touche Entrée private initMessageEvents() {
const messageInput = document.getElementById('message-input'); // Pour le bouton Send
if (messageInput) { const sendButton = this.shadowRoot?.querySelector('#send-button');
messageInput.addEventListener('keypress', (event: KeyboardEvent) => { if (sendButton) {
if (event.key === 'Enter' && !event.shiftKey) { sendButton.addEventListener('click', () => this.sendMessage());
event.preventDefault();
this.sendMessage();
} }
});
}
}
private initFileUpload() { // Pour la touche Entrée
const fileInput = document.getElementById('file-input') as HTMLInputElement; const messageInput = this.shadowRoot?.querySelector('#message-input');
if (fileInput) { if (messageInput) {
fileInput.addEventListener('change', (event: Event) => { messageInput.addEventListener('keypress', (event: Event) => {
const target = event.target as HTMLInputElement; const keyEvent = event as KeyboardEvent; // Cast en KeyboardEvent
if (target.files && target.files.length > 0) { if (keyEvent.key === 'Enter' && !keyEvent.shiftKey) {
this.sendFile(target.files[0]); event.preventDefault();
this.sendMessage();
}
});
} }
});
}
}
///////////////////// 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 = '<div class="no-notification">No notifications available</div>';
return;
} }
// Add each notification to the list private initFileUpload() {
this.notifications.forEach((notif, index) => { const fileInput = this.shadowRoot?.querySelector('#file-input') as HTMLInputElement;
const notifElement = document.createElement('div'); if (fileInput) {
notifElement.className = 'notification-item'; fileInput.addEventListener('change', (event: Event) => {
notifElement.textContent = `${notif.text} at ${notif.time}`; const target = event.target as HTMLInputElement;
notifElement.onclick = () => { if (target.files && target.files.length > 0) {
this.loadMemberChat(notif.memberId); this.sendFile(target.files[0]);
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 ///////////////////// Notification module /////////////////////
private addNotification(memberId: string, message: Message) { // Delete a notification
// Creating a new notification private removeNotification(index: number) {
const notification = { this.notifications?.splice(index, 1); // Ajout de ?.
memberId, this.renderNotifications();
text: `New message from Member ${memberId}: ${message.text}`, this.updateNotificationBadge();
time: message.time, }
}; // Show notifications
private renderNotifications() {
if (!this.notificationBoard) return;
// Added notification to list and interface // Reset the interface
this.notifications.push(notification); this.notificationBoard.innerHTML = '';
this.renderNotifications();
this.updateNotificationBadge();
}
// Send a messsage // Displays "No notifications available" if there are no notifications
private sendMessage() { if (this.notifications.length === 0) {
const messageInput = document.getElementById('message-input') as HTMLInputElement; this.notificationBoard.innerHTML = '<div class="no-notification">No notifications available</div>';
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; if (!messageInput) return;
const messageText = messageInput.value.trim(); const messageText = messageInput.value.trim();
if (messageText === '' || this.selectedMemberId === null) { if (messageText === '' || this.selectedMemberId === null) {
return; return;
} }
const newMessage: Message = { const newMessage: Message = {
id: Date.now(), id: Date.now(),
sender: '4NK', sender: "4NK",
text: messageText, text: messageText,
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
type: 'text' as const, type: 'text' as const
}; };
// Add and display the message immediately // Add and display the message immediately
messageStore.addMessage(this.selectedMemberId, newMessage); messageStore.addMessage(this.selectedMemberId, newMessage);
@ -177,223 +224,224 @@ class ChatElement extends HTMLElement {
// Automatic response after 2 seconds // Automatic response after 2 seconds
setTimeout(() => { setTimeout(() => {
if (this.selectedMemberId) { if (this.selectedMemberId) {
const autoReply = this.generateAutoReply(`Member ${this.selectedMemberId}`); const autoReply = this.generateAutoReply(`Member ${this.selectedMemberId}`);
messageStore.addMessage(this.selectedMemberId, autoReply); messageStore.addMessage(this.selectedMemberId, autoReply);
this.messagesMock = messageStore.getMessages(); this.messagesMock = messageStore.getMessages();
this.loadMemberChat(this.selectedMemberId); this.loadMemberChat(this.selectedMemberId);
this.addNotification(this.selectedMemberId, autoReply); this.addNotification(this.selectedMemberId, autoReply);
}
}, 2000);
}
// Scroll down the conversation after loading messages
private scrollToBottom(container: HTMLElement) {
container.scrollTop = container.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,
};
} }
}); }, 2000);
});
const chatHeader = document.getElementById('chat-header');
const messagesContainer = document.getElementById('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 = `<a href="${message.fileData}" download="${message.fileName}" target="_blank">${message.fileName}</a>`;
messageContent.classList.add('user');
} else {
messageContent.innerHTML = `<strong>${message.sender}</strong>: ${message.text} <span style="float: right;">${message.time}</span>`;
if (message.sender === '4NK') {
messageContent.classList.add('user');
}
}
messageElement.appendChild(messageContent);
messagesContainer.appendChild(messageElement);
});
} }
this.scrollToBottom(messagesContainer); // Scroll down the conversation after loading messages
} private scrollToBottom(container: Element) {
(container as HTMLElement).scrollTop = (container as HTMLElement).scrollHeight;
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 // Load the list of members
const roleNameAlt = container.querySelector('.role-name')?.textContent; private loadMemberChat(memberId: string | number) {
console.log('Role name from span:', roleNameAlt); this.selectedMemberId = String(memberId);
const memberMessages = this.messagesMock.find(m => String(m.memberId) === String(memberId));
if (!container.querySelector('.folder-icon')) { // Find the process and the role of the member
const folderButton = document.createElement('span'); let memberInfo = { processName: '', roleName: '', memberName: '' };
folderButton.className = 'folder-icon'; 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
};
}
});
});
folderButton.addEventListener('click', (event) => { const chatHeader = this.shadowRoot?.querySelector('#chat-header');
event.stopPropagation(); const messagesContainer = this.shadowRoot?.querySelector('#messages');
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 (!chatHeader || !messagesContainer) return;
if (role) {
console.log('Found role:', role); chatHeader.textContent = `Chat with ${memberInfo.roleName} ${memberInfo.memberName} from ${memberInfo.processName}`;
} else { messagesContainer.innerHTML = '';
console.error('Role not found. Name:', roleName);
console.error('Available roles:', group.roles); 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 = `<a href="${message.fileData}" download="${message.fileName}" target="_blank">${message.fileName}</a>`;
messageContent.classList.add('user');
} else {
messageContent.innerHTML = `<strong>${message.sender}</strong>: ${message.text} <span style="float: right;">${message.time}</span>`;
if (message.sender === "4NK") {
messageContent.classList.add('user');
}
}
messageElement.appendChild(messageContent);
messagesContainer.appendChild(messageElement);
});
} }
});
container.appendChild(folderButton);
this.scrollToBottom(messagesContainer);
} }
});
(roleList as HTMLElement).style.display = (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none'; 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);
} }
}
private loadGroupList(): void {
const groupList = document.getElementById('group-list');
if (!groupList) return;
groupsMock.forEach((group) => { // Toggle the list of Roles
const li = document.createElement('li'); private toggleRoles(group: Group, groupElement: HTMLElement) {
li.className = 'group-list-item'; console.log('=== toggleRoles START ===');
console.log('Group:', group.name);
console.log('Group roles:', group.roles); // Afficher tous les rôles disponibles
// Create a flex container for the name and the icon let roleList = groupElement.querySelector('.role-list');
const container = document.createElement('div'); console.log('Existing roleList:', roleList);
container.className = 'group-item-container';
// Span for the process name if (roleList) {
const nameSpan = document.createElement('span'); const roleItems = roleList.querySelectorAll('.role-item');
nameSpan.textContent = group.name; roleItems.forEach(roleItem => {
nameSpan.className = 'process-name'; console.log('Processing roleItem:', roleItem.innerHTML); // Voir le contenu HTML complet
// Add click event to show roles let container = roleItem.querySelector('.role-item-container');
nameSpan.addEventListener('click', (event) => { if (!container) {
event.stopPropagation(); container = document.createElement('div');
this.toggleRoles(group, li); container.className = 'role-item-container';
});
// Assemble the elements // Créer un span pour le nom du rôle
container.appendChild(nameSpan); const nameSpan = document.createElement('span');
li.appendChild(container); nameSpan.className = 'role-name';
nameSpan.textContent = roleItem.textContent?.trim() || '';
// Create and append the role list container container.appendChild(nameSpan);
const roleList = document.createElement('ul'); roleItem.textContent = '';
roleList.className = 'role-list'; roleItem.appendChild(container);
roleList.style.display = 'none'; }
// Add roles for this process // Récupérer le nom du rôle
group.roles.forEach((role) => { const roleName = roleItem.textContent?.trim();
const roleItem = document.createElement('li'); console.log('Role name from textContent:', roleName);
roleItem.className = 'role-item';
roleItem.textContent = role.name;
roleItem.onclick = (event) => {
event.stopPropagation();
this.toggleMembers(role, roleItem);
};
roleList.appendChild(roleItem);
});
li.appendChild(roleList); // Alternative pour obtenir le nom du rôle
groupList.appendChild(li); const roleNameAlt = container.querySelector('.role-name')?.textContent;
}); console.log('Role name from span:', roleNameAlt);
}
// Function to manage the list of users if (!container.querySelector('.folder-icon')) {
private toggleUserList() { const folderButton = document.createElement('span');
const userList = getCorrectDOM('userList'); folderButton.className = 'folder-icon';
if (!userList) return;
if (!(userList as HTMLElement).classList.contains('show')) { folderButton.addEventListener('click', (event) => {
(userList as HTMLElement).innerHTML = membersMock event.stopPropagation();
.map( console.log('Clicked role name:', roleName);
(member) => ` 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(): void {
const groupList = this.shadowRoot?.querySelector('#group-list');
if (!groupList) return;
groupsMock.forEach(group => {
const li = document.createElement('li');
li.className = 'group-list-item';
// Create a flex container for the name and the icon
const container = document.createElement('div');
container.className = 'group-item-container';
// Span for the process name
const nameSpan = document.createElement('span');
nameSpan.textContent = group.name;
nameSpan.className = 'process-name';
// Add click event to show roles
nameSpan.addEventListener('click', (event) => {
event.stopPropagation();
this.toggleRoles(group, li);
});
// Assemble the elements
container.appendChild(nameSpan);
li.appendChild(container);
// Create and append the role list container
const roleList = document.createElement('ul');
roleList.className = 'role-list';
roleList.style.display = 'none';
// Add roles for this process
group.roles.forEach(role => {
const roleItem = document.createElement('li');
roleItem.className = 'role-item';
roleItem.textContent = role.name;
roleItem.onclick = (event) => {
event.stopPropagation();
this.toggleMembers(role, roleItem);
};
roleList.appendChild(roleItem);
});
li.appendChild(roleList);
groupList.appendChild(li);
});
}
// 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 => `
<div class="user-list-item" onclick="switchUser('${member.id}')"> <div class="user-list-item" onclick="switchUser('${member.id}')">
<span class="user-avatar">${member.avatar}</span> <span class="user-avatar">${member.avatar}</span>
<div> <div>
@ -401,127 +449,116 @@ class ChatElement extends HTMLElement {
<span class="user-email">${member.email}</span> <span class="user-email">${member.email}</span>
</div> </div>
</div> </div>
`, `).join('');
) }
.join(''); (userList as HTMLElement).classList.toggle('show');
} }
(userList as HTMLElement).classList.toggle('show');
}
private switchUser(userId: string | number) { private switchUser(userId: string | number) {
const user = membersMock.find((member) => member.id === userId); const user = membersMock.find(member => member.id === userId);
if (!user) return; if (!user) return;
currentUser = user; currentUser = user;
this.updateCurrentUserDisplay(); this.updateCurrentUserDisplay();
const userList = getCorrectDOM('userList') as HTMLElement; const userList = getCorrectDOM('userList') as HTMLElement;
userList?.classList.remove('show'); userList?.classList.remove('show');
} }
// Function to update the display of the current user // Function to update the display of the current user
private updateCurrentUserDisplay() { private updateCurrentUserDisplay() {
const userDisplay = getCorrectDOM('current-user') as HTMLElement; const userDisplay = getCorrectDOM('current-user') as HTMLElement;
if (userDisplay) { if (userDisplay) {
userDisplay.innerHTML = ` userDisplay.innerHTML = `
<div class="current-user-info"> <div class="current-user-info">
<span class="user-avatar">${currentUser.avatar}</span> <span class="user-avatar">${currentUser.avatar}</span>
<span class="user-name">${currentUser.name}</span> <span class="user-name">${currentUser.name}</span>
</div> </div>
`; `;
}
}
// 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
};
} }
}
// 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 // Send a file
private sendFile(file: File) { private sendFile(file: File) {
console.log('SendFile called with file:', file); console.log('SendFile called with file:', file);
const reader = new FileReader(); const reader = new FileReader();
reader.onloadend = () => { reader.onloadend = () => {
const fileData = reader.result; const fileData = reader.result;
const fileName = file.name; const fileName = file.name;
console.log('File loaded:', fileName); console.log('File loaded:', fileName);
if (this.selectedMemberId) { if (this.selectedMemberId) {
messageStore.addMessage(this.selectedMemberId, { messageStore.addMessage(this.selectedMemberId, {
id: Date.now(), id: Date.now(),
sender: '4NK', sender: "4NK",
fileName: fileName, fileName: fileName,
fileData: fileData, fileData: fileData,
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
type: 'file', 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 => {
}); });
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 // Gestionnaire d'événements pour le chat
const sendBtn = this.shadowRoot?.querySelector('#send-button'); const sendBtn = this.shadowRoot?.querySelector('#send-button');
if (sendBtn) { if (sendBtn) {
sendBtn.addEventListener('click', this.sendMessage.bind(this)); sendBtn.addEventListener('click', this.sendMessage.bind(this));
} }
const messageInput = this.shadowRoot?.querySelector('#message-input'); const messageInput = this.shadowRoot?.querySelector('#message-input');
if (messageInput) { if (messageInput) {
messageInput.addEventListener('keypress', (event: Event) => { messageInput.addEventListener('keypress', (event: Event) => {
if ((event as KeyboardEvent).key === 'Enter') { if ((event as KeyboardEvent).key === 'Enter') {
event.preventDefault(); event.preventDefault();
this.sendMessage(); this.sendMessage();
}
});
} }
});
}
// Gestionnaire pour l'envoi de fichiers // Gestionnaire pour l'envoi de fichiers
const fileInput = this.shadowRoot?.querySelector('#file-input'); const fileInput = this.shadowRoot?.querySelector('#file-input');
if (fileInput) { if (fileInput) {
fileInput.addEventListener('change', (event: Event) => { fileInput.addEventListener('change', (event: Event) => {
const file = (event.target as HTMLInputElement).files?.[0]; const file = (event.target as HTMLInputElement).files?.[0];
if (file) { if (file) {
this.sendFile(file); this.sendFile(file);
}
});
} }
});
}
}
connectedCallback() {
if (this.shadowRoot) {
this.shadowRoot.innerHTML = `
<div class="container">
<div class="group-list">
<ul id="group-list"></ul>
</div>
<div class="chat-area">
<!-- ... reste du HTML de signature.html ... -->
</div>
</div>
`;
} }
this.messagesMock = messageStore.getMessages();
if (this.messagesMock.length === 0) {
messageStore.setMessages(initialMessagesMock); connectedCallback() {
this.messagesMock = messageStore.getMessages(); this.updateCurrentUserDisplay();
this.initializeEventListeners();
this.loadGroupList();
// Si un membre est sélectionné par défaut, charger ses messages
if (this.selectedMemberId) {
this.loadMemberChat(this.selectedMemberId);
}
} }
this.updateCurrentUserDisplay();
this.initializeEventListeners();
this.loadGroupList();
}
} }
customElements.define('chat-element', ChatElement); customElements.define('chat-element', ChatElement);
export { ChatElement }; export { ChatElement };

View File

@ -1,59 +1,58 @@
import { SignatureElement } from './signature'; import { SignatureElement } from './signature';
import signatureCss from '../../../public/style/signature.css?raw'; import signatureCss from '../../../public/style/signature.css?raw'
import Services from '../../services/service.js'; import Services from '../../services/service.js'
class SignatureComponent extends HTMLElement { class SignatureComponent extends HTMLElement {
_callback: any; _callback: any
signatureElement: SignatureElement | null = null; signatureElement: SignatureElement | null = null;
constructor() { constructor() {
super(); super();
console.log('INIT'); console.log('INIT')
this.attachShadow({ mode: 'open' }); this.attachShadow({ mode: 'open' });
this.signatureElement = this.shadowRoot?.querySelector('signature-element') || null; this.signatureElement = this.shadowRoot?.querySelector('signature-element') || null;
}
connectedCallback() {
console.log('CALLBACKs');
this.render();
this.fetchData();
if (!customElements.get('signature-element')) {
customElements.define('signature-element', SignatureElement);
} }
}
async fetchData() { connectedCallback() {
if ((import.meta as any).env.VITE_IS_INDEPENDANT_LIB === false) { console.log('CALLBACKs')
const data = await (window as any).myService?.getProcesses(); this.render();
} else { this.fetchData();
const service = await Services.getInstance();
const data = await service.getProcesses(); if (!customElements.get('signature-element')) {
customElements.define('signature-element', SignatureElement);
}
} }
}
set callback(fn) { async fetchData() {
if (typeof fn === 'function') { if ((import.meta as any).env.VITE_IS_INDEPENDANT_LIB === false) {
this._callback = fn; const data = await (window as any).myService?.getProcesses();
} else { } else {
console.error('Callback is not a function'); const service = await Services.getInstance()
const data = await service.getProcesses();
}
} }
}
get callback() { set callback(fn) {
return this._callback; if (typeof fn === 'function') {
} this._callback = fn;
} else {
render() { console.error('Callback is not a function');
if (this.shadowRoot) { }
// Créer l'élément signature-element }
const signatureElement = document.createElement('signature-element');
this.shadowRoot.innerHTML = `<style>${signatureCss}</style>`; get callback() {
this.shadowRoot.appendChild(signatureElement); return this._callback;
}
render() {
if(this.shadowRoot) {
const signatureElement = document.createElement('signature-element');
this.shadowRoot.innerHTML = `<style>${signatureCss}</style>`;
this.shadowRoot.appendChild(signatureElement);
}
} }
}
} }
export { SignatureComponent }; export { SignatureComponent }
customElements.define('signature-component', SignatureComponent); customElements.define('signature-component', SignatureComponent);

View File

@ -1,46 +1,12 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" /> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Signatures</title> <title>Signatures</title>
<link rel="stylesheet" href="../../../public/style/signature.css" /> </head>
</head> <body>
<signature-component></signature-component>
<script type="module" src="./signature.ts"></script>
</body>
<body>
<!-- Main content-->
<div class="container">
<!-- List of groups -->
<div class="group-list">
<ul id="group-list"></ul>
</div>
<!-- Chat area -->
<div class="chat-area">
<div class="chat-header" id="chat-header">
<!-- Chat title -->
</div>
<div class="messages" id="messages">
<!-- Messages -->
</div>
<!-- Input area -->
<div class="input-area">
<label for="file-input" class="attachment-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path
d="M13.514 2.444l-10.815 10.785c-.449.449-.678 1.074-.625 1.707l.393 4.696c.041.479.422.86.9.9l4.697.394c.633.053 1.258-.177 1.707-.626l11.875-11.844c.196-.196.195-.512 0-.707l-3.536-3.536c-.195-.195-.511-.196-.707 0l-8.878 8.848c-.162.162-.253.382-.253.611v.725c0 .184.148.332.332.332h.725c.229 0 .448-.092.61-.254l7.11-7.08 1.415 1.415-7.386 7.354c-.375.375-.885.586-1.414.586h-2.414c-.555 0-1-.448-1-1v-2.414c0-.53.211-1.039.586-1.414l9.506-9.477c.781-.781 2.049-.781 2.829-.001l4.243 4.243c.391.391.586.902.586 1.414 0 .512-.196 1.025-.587 1.416l-12.35 12.319c-.748.747-1.76 1.164-2.81 1.164-.257 0-6.243-.467-6.499-.487-.664-.052-1.212-.574-1.268-1.267-.019-.242-.486-6.246-.486-6.499 0-1.05.416-2.062 1.164-2.811l10.936-10.936 1.414 1.444z"
/>
</svg>
</label>
<input type="file" id="file-input" style="display: none" />
<textarea id="message-input" rows="3" placeholder="Type your message..."></textarea>
<button id="send-button">Send</button>
</div>
</div>
</div>
<script type="module" src="./signature.ts?ts"></script>
</body>
</html> </html>

File diff suppressed because it is too large Load Diff