ihm_client/src/pages/signature/signature.ts
2024-11-22 12:49:06 +01:00

1210 lines
47 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

declare global {
interface Window {
toggleUserList: typeof toggleUserList;
switchUser: typeof switchUser;
closeProcessDetails: typeof closeProcessDetails;
loadMemberChat: typeof loadMemberChat;
closeRoleDocuments: typeof closeRoleDocuments;
newRequest: typeof newRequest;
submitRequest: typeof submitRequest;
closeNewRequest: typeof closeNewRequest;
removeMember: typeof removeMember;
closeModal: typeof closeModal;
submitDocumentRequest: typeof submitDocumentRequest;
submitNewDocument: typeof submitNewDocument;
}
}
import { groupsMock } from '../../mocks/mock-signature/groupsMock';
import { messagesMock as initialMessagesMock } from '../../mocks/mock-signature/messagesMock';
import { membersMock } from '../../mocks/mock-signature/membersMocks';
import {
Group,
Message,
MemberMessages,
DocumentSignature,
RequestParams,
Notification as ImportedNotification
} from '../../models/signature.models';
import { messageStore } from '../../utils/messageMock';
import { showAlert } from '../account/account';
let currentUser = membersMock[0];
// Fonction pour gérer la liste des utilisateurs
function toggleUserList() {
const userList = document.getElementById('userList');
if (!userList) return;
if (!userList.classList.contains('show')) {
// Remplir la liste des utilisateurs
userList.innerHTML = membersMock.map(member => `
<div class="user-list-item" onclick="switchUser('${member.id}')">
<span class="user-avatar">${member.avatar}</span>
<div>
<span class="user-name">${member.name}</span>
<span class="user-email">${member.email}</span>
</div>
</div>
`).join('');
}
userList?.classList.toggle('show');
}
// Fonction pour changer d'utilisateur
function switchUser(userId: string) {
const user = membersMock.find(member => member.id === userId);
if (!user) return;
currentUser = user;
updateCurrentUserDisplay();
// Recharger la vue si nécessaire
const userList = document.getElementById('userList');
userList?.classList.remove('show');
}
// Fonction pour mettre à jour l'affichage de l'utilisateur
function updateCurrentUserDisplay() {
const userDisplay = document.getElementById('current-user');
if (userDisplay) {
userDisplay.innerHTML = `
<div class="current-user-info">
<span class="user-avatar">${currentUser.avatar}</span>
<span class="user-name">${currentUser.name}</span>
</div>
`;
}
}
// Ajouter les fonctions au scope global
window.toggleUserList = toggleUserList;
window.switchUser = switchUser;
// Initialiser l'affichage de l'utilisateur courant au chargement
document.addEventListener('DOMContentLoaded', () => {
updateCurrentUserDisplay();
// Fermer la liste si on clique ailleurs
document.addEventListener('click', (event) => {
const userList = document.getElementById('userList');
const userSwitchBtn = document.getElementById('userSwitchBtn');
if (userSwitchBtn && userList && !userSwitchBtn.contains(event.target as Node) && !userList.contains(event.target as Node)) {
userList.classList.remove('show');
}
});
// Initialiser les groupes en localStorage s'ils n'existent pas
if (!localStorage.getItem('groups')) {
localStorage.setItem('groups', JSON.stringify(groupsMock));
}
});
let messagesMock = messageStore.getMessages();
if (messagesMock.length === 0) {
messageStore.setMessages(initialMessagesMock);
messagesMock = messageStore.getMessages();
}
let selectedMemberId: string | null = null;
// Load the list of groups
function loadGroupList() {
const groupList = document.getElementById('group-list');
if (!groupList) return;
groupsMock.forEach(group => {
const li = document.createElement('li');
li.className = 'group-list-item';
// Créer un conteneur flex pour le nom et l'icône
const container = document.createElement('div');
container.className = 'group-item-container';
// Span pour le nom du processus
const nameSpan = document.createElement('span');
nameSpan.textContent = group.name;
nameSpan.className = 'process-name';
nameSpan.onclick = (event) => {
event.stopPropagation();
toggleRoles(group, li);
};
// Ajouter l'icône ⚙️ avec un ID unique
const settingsIcon = document.createElement('span');
settingsIcon.textContent = '⚙️';
settingsIcon.className = 'settings-icon';
settingsIcon.id = `settings-${group.id}`; // ID unique basé sur l'ID du groupe
// Créer une div pour la vue détaillée avec un ID unique correspondant
const detailsArea = document.createElement('div');
detailsArea.id = `process-details-${group.id}`;
detailsArea.className = 'process-details';
detailsArea.style.display = 'none';
settingsIcon.onclick = (event) => {
event.stopPropagation();
showProcessDetails(group, group.id);
};
// Assembler les éléments
container.appendChild(nameSpan);
container.appendChild(settingsIcon);
li.appendChild(container);
groupList.appendChild(li);
});
}
// Toggle the list of Roles
function toggleRoles(group: Group, groupElement: HTMLElement) {
let roleList = groupElement.querySelector('.role-list');
if (roleList) {
(roleList as HTMLElement).style.display = (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none';
return;
}
roleList = document.createElement('ul');
roleList.className = 'role-list';
group.roles.forEach(role => {
const roleItem = document.createElement('li');
// Créer un conteneur flex pour le nom et l'icône
const container = document.createElement('div');
container.className = 'role-item-container';
container.style.display = 'flex';
container.style.justifyContent = 'space-between';
container.style.alignItems = 'center';
// Span pour le nom du rôle
const nameSpan = document.createElement('span');
nameSpan.textContent = role.name;
// Bouton dossier
const folderButton = document.createElement('span');
folderButton.textContent = '📁';
folderButton.className = 'folder-icon';
folderButton.onclick = (event) => {
event.stopPropagation();
showRoleDocuments(role, group);
};
// Assembler les éléments
container.appendChild(nameSpan);
container.appendChild(folderButton);
roleItem.appendChild(container);
roleItem.onclick = (event) => {
event.stopPropagation();
toggleMembers(role, roleItem);
};
roleList.appendChild(roleItem);
});
groupElement.appendChild(roleList);
}
// Toggle the list of membres
function 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();
loadMemberChat(member.id.toString());
};
memberList.appendChild(memberItem);
});
roleElement.appendChild(memberList);
}
// Load the list of members
function loadMemberChat(memberId: string | number) {
selectedMemberId = String(memberId);
const memberMessages = messagesMock.find(m => String(m.memberId) === String(memberId));
// Trouver le processus et le rôle du membre
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 = 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);
});
}
scrollToBottom(messagesContainer);
}
// Scroll down the conversation after loading messages
function scrollToBottom(container: HTMLElement) {
container.scrollTop = container.scrollHeight;
}
// Generate an automatic response
function 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 messsage
function sendMessage() {
const messageInput = document.getElementById('message-input') as HTMLInputElement;
if (!messageInput) return;
const messageText = messageInput.value.trim();
if (messageText === '' || 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
};
// Ajouter et afficher le message immédiatement
messageStore.addMessage(selectedMemberId, newMessage);
messagesMock = messageStore.getMessages();
loadMemberChat(selectedMemberId);
// Réinitialiser l'input
messageInput.value = '';
// Réponse automatique après 2 secondes
setTimeout(() => {
if (selectedMemberId) {
const autoReply = generateAutoReply(`Member ${selectedMemberId}`);
messageStore.addMessage(selectedMemberId, autoReply);
messagesMock = messageStore.getMessages();
loadMemberChat(selectedMemberId);
addNotification(selectedMemberId, autoReply);
}
}, 2000);
}
// Add an event for the submit button
const sendBtn = document.getElementById('send-button');
if (sendBtn) sendBtn.onclick = sendMessage;
const messageInput = document.getElementById('message-input');
if (messageInput) {
messageInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
sendMessage();
}
});
}
// Send a file
function sendFile(file: File) {
const reader = new FileReader();
reader.onloadend = function () {
const fileData = reader.result;
const fileName = file.name;
const newFileMessage = {
id: Date.now(),
sender: "4NK",
fileName: fileName,
fileData: fileData,
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
type: 'file'
};
if (selectedMemberId) {
messageStore.addMessage(selectedMemberId, newFileMessage);
}
messagesMock = messageStore.getMessages();
if (selectedMemberId) {
loadMemberChat(selectedMemberId);
}
};
reader.readAsDataURL(file);
}
// Managing the sent file
document.getElementById('file-input')?.addEventListener('change', function (event) {
const file = (event.target as HTMLInputElement).files?.[0];
if (file) {
sendFile(file);
}
});
///////////////////// Notification module /////////////////////
const notificationBadge = document.querySelector('.notification-badge');
const notificationBoard = document.getElementById('notification-board');
const notificationBell = document.getElementById('notification-bell');
interface LocalNotification {
memberId: string;
text: string;
time: string;
}
let notifications: LocalNotification[] = [];
let unreadCount = 0;
// Update notification badge
function updateNotificationBadge() {
if (!notificationBadge) return;
const count = notifications.length;
notificationBadge.textContent = count > 99 ? '+99' : count.toString();
(notificationBadge as HTMLElement).style.display = count > 0 ? 'block' : 'none';
}
// Add notification
function 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
notifications.push(notification);
renderNotifications();
updateNotificationBadge();
}
// Show notifications
function renderNotifications() {
if (!notificationBoard) return;
// Reset the interface
notificationBoard.innerHTML = '';
// Displays "No notifications available" if there are no notifications
if (notifications.length === 0) {
notificationBoard.innerHTML = '<div class="no-notification">No notifications available</div>';
return;
}
// Add each notification to the list
notifications.forEach((notif, index) => {
const notifElement = document.createElement('div');
notifElement.className = 'notification-item';
notifElement.textContent = `${notif.text} at ${notif.time}`;
notifElement.onclick = () => {
loadMemberChat(notif.memberId);
removeNotification(index);
};
notificationBoard.appendChild(notifElement);
});
}
// Delete a notification
function removeNotification(index: number) {
notifications.splice(index, 1);
renderNotifications();
updateNotificationBadge();
}
// Adds an event for deploying the notification list
if (notificationBell && notificationBoard) {
notificationBell.onclick = () => {
notificationBoard.style.display = notificationBoard.style.display === 'block' ? 'none' : 'block';
};
}
// Close the notification board when clicking outside of it
document.addEventListener('click', (event) => {
if (notificationBoard && notificationBoard.style.display === 'block' &&
!notificationBoard.contains(event.target as Node) &&
notificationBell && !notificationBell.contains(event.target as Node)) {
notificationBoard.style.display = 'none';
}
});
// ------------------ PROCESS DETAILS ------------------
// Fonction pour afficher les détails du processus
function showProcessDetails(group: Group, groupId: number) {
console.log('Showing details for group:', groupId);
// Fermer toutes les vues de processus existantes
const allDetailsAreas = document.querySelectorAll('.process-details');
allDetailsAreas.forEach(area => {
(area as HTMLElement).style.display = 'none';
});
const container = document.querySelector('.container');
if (!container) {
console.error('Container not found');
return;
}
let detailsArea = document.getElementById(`process-details-${groupId}`);
if (!detailsArea) {
detailsArea = document.createElement('div');
detailsArea.id = `process-details-${groupId}`;
detailsArea.className = 'process-details';
container.appendChild(detailsArea);
}
if (detailsArea) {
detailsArea.style.display = 'block';
detailsArea.innerHTML = `
<div class="process-details-header">
<h2>${group.name}</h2>
<div class="header-buttons">
</div>
</div>
<div class="process-details-content">
<div class="details-section">
<h3>Description</h3>
<p>${group.description || 'No description available'}</p>
</div>
<div class="details-section">
<h3>Roles and Documents</h3>
${group.roles.map(role => `
<div class="role-section">
<h4>${role.name}</h4>
<div class="documents-grid">
${(role.documents || []).length > 0 ?
(role.documents || []).map(document => {
const totalSignatures = document.signatures.length;
const signedCount = document.signatures.filter((sig: DocumentSignature) => sig.signed).length;
const percentage = totalSignatures > 0 ? (signedCount / totalSignatures) * 100 : 0;
// Détection d'un document vierge
const isVierge = !document.createdAt ||
!document.deadline ||
document.signatures.length === 0;
return `
<div class="document-card ${document.visibility} ${isVierge ? 'vierge' : ''}">
<div class="document-header">
<h4>${isVierge ? `⚠️ ${document.name}` : document.name}</h4>
<span class="document-visibility">${document.visibility}</span>
</div>
<div class="document-info">
${!isVierge ? `
<p class="document-date">Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}</p>
<p class="document-deadline">Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}</p>
<p class="document-duration">Duration: ${calculateDuration(document.createdAt || '', document.deadline || '')} days</p>
` : '<p>Document vierge - En attente de création</p>'}
</div>
${!isVierge ? `
<div class="document-signatures">
<h5>Signatures:</h5>
<div class="signatures-list">
${document.signatures.map((sig: DocumentSignature) => `
<div class="signature-item ${sig.signed ? 'signed' : 'pending'}">
<span class="signer-name">${sig.member.name}</span>
<span class="signature-status">
${sig.signed ?
`✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'Unknown date'}` :
' Pending'}
</span>
</div>
`).join('')}
</div>
<div class="progress-bar">
<div class="progress" style="width: ${percentage}%;"></div>
</div>
<p>${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)</p>
</div>
` : ''}
</div>
`;
}).join('')
: '<p class="no-documents">No documents available for this role</p>'}
</div>
</div>
`).join('')}
</div>
<div class="details-section">
<h3>Members by Role</h3>
<div class="roles-grid">
${group.roles.map(role => `
<div class="role-block">
<h4>${role.name}</h4>
<ul class="members-list">
${role.members.map(member => `
<li onclick="loadMemberChat(${member.id})">${member.name}</li>
`).join('')}
</ul>
</div>
`).join('')}
</div>
</div>
</div>
`;
/**const newRequestBtn = document.createElement('button');
newRequestBtn.className = 'new-request-btn';
newRequestBtn.textContent = 'New request';
newRequestBtn.onclick = () => newRequest(group);**/
const newCloseProcessButton = document.createElement('button');
newCloseProcessButton.className = 'close-btn';
newCloseProcessButton.textContent = 'x';
newCloseProcessButton.addEventListener('click', () => closeProcessDetails(groupId));
const headerButtons = detailsArea.querySelector('.header-buttons');
if (headerButtons) {
/**headerButtons.appendChild(newRequestBtn);**/
headerButtons.appendChild(newCloseProcessButton);
}
}
}
// Fonction pour fermer les détails et revenir au chat
function closeProcessDetails(groupId: number) {
const detailsArea = document.getElementById(`process-details-${groupId}`);
const chatArea = document.getElementById('chat-area');
if (detailsArea) {
detailsArea.style.display = 'none';
}
if (chatArea) {
chatArea.style.display = 'block';
}
}
window.closeProcessDetails = closeProcessDetails;
window.loadMemberChat = loadMemberChat;
// Nouvelle fonction pour afficher les documents d'un rôle
function showRoleDocuments(role: {
name: string;
documents?: Array<{
name: string;
visibility: string;
createdAt: string | null | undefined;
deadline: string | null | undefined;
signatures: DocumentSignature[];
id: number;
description?: string;
status?: string;
}>;
id?: number;
}, group: Group) {
console.log('Showing documents for role:', role.name, 'in group:', group.name);
// Fermer d'abord toutes les vues de documents existantes
const allDetailsAreas = document.querySelectorAll('.process-details');
allDetailsAreas.forEach(area => {
area.remove();
});
const container = document.querySelector('.container');
if (!container) {
console.error('Container not found');
return;
}
// Créer une nouvelle zone de détails
const detailsArea = document.createElement('div');
detailsArea.id = `role-documents-${role.name}`;
detailsArea.className = 'process-details';
detailsArea.innerHTML = `
<div class="process-details-header">
<h2>${role.name} Documents</h2>
<div class="header-buttons">
<button class="close-btn" onclick="closeRoleDocuments('${role.name}')">✕</button>
</div>
</div>
<div class="process-details-content">
<div class="details-section">
<h3>Documents</h3>
<div class="documents-grid">
${(role.documents || []).length > 0 ?
(role.documents || []).map(document => {
const totalSignatures = document.signatures.length;
const signedCount = document.signatures.filter((sig: DocumentSignature) => sig.signed).length;
const percentage = totalSignatures > 0 ? (signedCount / totalSignatures) * 100 : 0;
const isVierge = !document.createdAt ||
!document.deadline ||
document.signatures.length === 0;
return `
<div class="document-card ${document.visibility} ${isVierge ? 'vierge' : ''}">
<div class="document-header">
<h4>${isVierge ? `⚠️ ${document.name}` : document.name}</h4>
<span class="document-visibility">${document.visibility}</span>
</div>
<div class="document-info">
${!isVierge ? `
<p class="document-date">Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}</p>
<p class="document-deadline">Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}</p>
<p class="document-duration">Duration: ${calculateDuration(document.createdAt, document.deadline)} days</p>
<div class="document-signatures">
<h5>Signatures:</h5>
<div class="signatures-list">
${document.signatures.map((sig: DocumentSignature) => `
<div class="signature-item ${sig.signed ? 'signed' : 'pending'}">
<span class="signer-name">${sig.member.name}</span>
<span class="signature-status">
${sig.signed ?
`✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'Unknown date'}` :
'⌛ Pending'}
</span>
</div>
`).join('')}
</div>
<div class="progress-bar">
<div class="progress" style="width: ${percentage}%;"></div>
</div>
<p>${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)</p>
</div>
` : `
<p>Document vierge - En attente de création</p>
<button class="new-request-btn" onclick="newRequest({
processId: ${group.id},
processName: '${group.name}',
roleId: ${role.id},
roleName: '${role.name}',
documentId: ${document.id},
documentName: '${document.name}'
})">New request</button>
`}
</div>
</div>
`;
}).join('')
: '<p class="no-documents">No documents available for this role</p>'
}
</div>
</div>
</div>
`;
container.appendChild(detailsArea);
}
// Fonction pour fermer la vue des documents d'un rôle
function closeRoleDocuments(roleName: string) {
const detailsArea = document.getElementById(`role-documents-${roleName}`);
if (detailsArea) {
detailsArea.remove();
}
}
window.closeRoleDocuments = closeRoleDocuments;
window.switchUser = switchUser;
// Fonction pour calculer la durée entre deux dates
function calculateDuration(startDate: string | null | undefined, endDate: string | null | undefined): number {
const start = new Date(startDate || '');
const end = new Date(endDate || '');
const duration = end.getTime() - start.getTime();
return Math.floor(duration / (1000 * 60 * 60 * 24));
}
window.newRequest = newRequest;
window.submitRequest = submitRequest;
window.closeNewRequest = closeNewRequest;
// Définir la fonction removeMember pour supprimer un membre par son ID
function removeMember(memberId: string | number) {
const memberElement = document.querySelector(`.selected-member[data-member-id="${memberId}"]`);
if (memberElement) {
memberElement.remove();
}
}
window.removeMember = removeMember;
// Fonction pour gérer la nouvelle demande
function newRequest(params: RequestParams) {
const modal = document.createElement('div');
modal.className = 'modal-overlay';
// Modifier cette partie pour chercher par name au lieu de id
const process = groupsMock.find(g => g.id === params.processId);
const role = process?.roles.find(r => r.name === params.roleName);
// Récupérer les membres du rôle
const roleMembers = role?.members ? role.members.map(member => member.id) : [];
modal.innerHTML = `
<div class="modal-document">
<div class="modal-content-document">
<div class="details-header">
<h2>New Document Request</h2>
<span class="document-context">
Process: ${params.processName} | Role: ${params.roleName} | Document: ${params.documentName}
</span>
</div>
<form id="newDocumentForm" class="document-form">
<input type="hidden" id="processId" value="${params.processId}">
<input type="hidden" id="roleId" value="${params.roleId}">
<input type="hidden" id="documentId" value="${params.documentId}">
<div class="form-left">
<div class="form-row">
<div class="form-group half">
<label for="documentName">Document Name*:</label>
<input type="text" id="documentName" required value="${params.documentName}">
</div>
<div class="form-group half">
<label for="createdAt">Created At:</label>
<input type="text" id="createdAt" value="${new Date().toLocaleDateString()}" readonly>
</div>
</div>
<div class="form-group">
<label for="description">Description:</label>
<textarea id="description"></textarea>
</div>
<div class="form-row">
<div class="form-group half">
<label for="visibility">Visibility*:</label>
<select id="visibility" required>
<option value="public">Public</option>
<option value="confidential">Confidential</option>
<option value="private">Private</option>
</select>
</div>
<div class="form-group half">
<label for="deadline">Deadline:</label>
<input type="date"
id="deadline"
min="${new Date().toISOString().split('T')[0]}"
required>
</div>
</div>
<div class="form-group">
<label>Import Files</label>
<div class="file-upload-container" id="dropZone">
<input type="file" id="fileInput" multiple accept=".pdf,.doc,.docx,.txt">
<p>Drop files here or click to select files</p>
</div>
<div id="fileList" class="file-list"></div>
</div>
<div class="form-group">
<label>Required Signatories:</label>
<div class="required-signatories">
${roleMembers.map(member => `
<div class="signatory-item locked">
<span class="member-name">${member}</span>
<span class="role-info">${params.roleName} - ${params.processName}</span>
<span class="lock-icon">🔒</span>
</div>
`).join('')}
</div>
</div>
</div>
</form>
<div class="modal-footer">
<button class="cancel-btn" onclick="closeModal(this)">Cancel</button>
<button class="confirm-btn" onclick="submitNewDocument(event)">Request</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
const dropZone = modal.querySelector('#dropZone') as HTMLDivElement;
const fileInput = modal.querySelector('#fileInput') as HTMLInputElement;
const fileList = modal.querySelector('#fileList') as HTMLDivElement;
// Rendre la zone cliquable
dropZone.addEventListener('click', () => {
fileInput.click();
});
// Gérer la sélection de fichiers
fileInput.addEventListener('change', (e: Event) => {
const target = e.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
handleFiles(target.files);
}
});
// Gérer le drag & drop
dropZone.addEventListener('dragover', (e: DragEvent) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e: DragEvent) => {
e.preventDefault();
dropZone.classList.remove('dragover');
if (e.dataTransfer?.files) {
handleFiles(e.dataTransfer.files);
}
});
function handleFiles(files: FileList) {
Array.from(files).forEach(file => {
// Vérifier si le fichier n'est pas déjà dans la liste
const existingFiles = fileList.querySelectorAll('.file-name');
const isDuplicate = Array.from(existingFiles).some(
existingFile => existingFile.textContent === file.name
);
if (!isDuplicate) {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.innerHTML = `
<div class="file-info">
<span class="file-name">${file.name}</span>
<span class="file-size">(${(file.size / 1024).toFixed(1)} KB)</span>
</div>
<button type="button" class="remove-file">×</button>
`;
// Ajouter l'événement de suppression
const removeBtn = fileItem.querySelector('.remove-file');
if (removeBtn) {
removeBtn.addEventListener('click', () => fileItem.remove());
}
fileList.appendChild(fileItem);
}
});
}
}
function closeModal(button: HTMLElement) {
const modalOverlay = button.closest('.modal-overlay');
if (modalOverlay) {
modalOverlay.remove();
}
}
window.closeModal = closeModal;
let selectedSignatories: DocumentSignature[] = [];
function submitNewDocument(event: Event) {
event.preventDefault();
const form = document.getElementById('newDocumentForm') as HTMLFormElement;
if (!form) {
showAlert('Form not found');
return;
}
// Get form values
const processId = Number((form.querySelector('#processId') as HTMLInputElement)?.value);
const documentId = Number((form.querySelector('#documentId') as HTMLInputElement)?.value);
const documentName = (form.querySelector('#documentName') as HTMLInputElement)?.value?.trim();
const description = (form.querySelector('#description') as HTMLTextAreaElement)?.value?.trim();
const deadline = (form.querySelector('#deadline') as HTMLInputElement)?.value;
const visibility = (form.querySelector('#visibility') as HTMLSelectElement)?.value;
// Validation
if (!documentName || !description || !deadline) {
showAlert('Please fill in all required fields');
return;
}
// Mettre à jour à la fois le mock local et le localStorage
let groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock));
const group = groups.find((g: Group) => g.id === processId);
if (!group) {
showAlert('Process not found');
return;
}
// Trouver le rôle qui contient le document
const role = group.roles.find((r: any) => {
return r.documents?.some((d: any) => d.id === documentId);
});
if (!role) {
showAlert('Role not found');
return;
}
const documentIndex = role.documents.findIndex((d: any) => d.id === documentId);
const updatedDocument = {
id: documentId,
name: documentName,
description: description,
createdAt: new Date().toISOString(),
deadline: deadline,
visibility: visibility,
status: "pending",
signatures: role.members.map((member: { id: string | number; name: string }) => ({
member: member,
signed: false,
signedAt: null
}))
};
// Mettre à jour le document dans le rôle
if (documentIndex !== -1) {
role.documents[documentIndex] = updatedDocument;
}
// Mettre à jour le localStorage et le mock global
localStorage.setItem('groups', JSON.stringify(groups));
Object.assign(groupsMock, groups);
// Fermer le modal
if (event.target instanceof HTMLElement) {
closeModal(event.target);
}
// Recharger la vue des documents
showRoleDocuments(role, group);
showAlert('Document updated successfully!');
}
window.submitNewDocument = submitNewDocument;
function submitRequest() {
showAlert("Request submitted!");
}
function closeNewRequest() {
const newRequestView = document.getElementById('new-request-view');
if (newRequestView) {
newRequestView.style.display = 'none';
// Optionnel : supprimer la vue du DOM
newRequestView.remove();
}
}
// N'oubliez pas d'exposer la fonction globalement
window.closeNewRequest = closeNewRequest;
document.addEventListener('DOMContentLoaded', function() {
const newRequestBtn = document.getElementById('newRequestBtn');
if (newRequestBtn) {
newRequestBtn.addEventListener('click', () => {
// Provide default/empty RequestParams when clicked
newRequest({
processId: 0,
processName: '',
roleId: 0,
roleName: '',
documentId: 0,
documentName: ''
});
});
}
});
function submitDocumentRequest(documentId: number) {
const createdAt = (document.getElementById('createdAt') as HTMLInputElement)?.value || '';
const deadline = (document.getElementById('deadline') as HTMLInputElement)?.value || '';
const visibility = (document.getElementById('visibility') as HTMLSelectElement)?.value || '';
const description = (document.getElementById('description') as HTMLTextAreaElement)?.value || '';
const selectedMembers = Array.from(
document.querySelectorAll('input[name="selected-members"]:checked')
).map(checkbox => (checkbox as HTMLInputElement).value);
if (!createdAt || !deadline || selectedMembers.length === 0) {
showAlert('Please fill in all required fields and select at least one member.');
return;
}
console.log('Document submission:', {
documentId,
createdAt,
deadline,
visibility,
description,
selectedMembers
});
showAlert('Document request submitted successfully!');
closeNewRequest();
}
window.submitDocumentRequest = submitDocumentRequest;
// Define allMembers using membersMock
const allMembers = membersMock.map(member => ({
id: member.id,
name: member.name,
roleName: 'Default Role' // Add role information if available in your membersMock
}));
const addMembersBtn = document.getElementById('addMembersBtn');
if (addMembersBtn) {
addMembersBtn.addEventListener('click', function() {
const selectedMembers: string[] = []; // Assuming member IDs are strings
// Logic to display a list of members to select
const membersToSelect = allMembers.map(member => `
<div class="member-checkbox">
<input type="checkbox" id="member-${member.id}" value="${member.id}">
<label for="member-${member.id}">${member.name} (${member.roleName})</label>
</div>
`).join('');
// Create a modal for member selection
const modalContent = `
<div class="modal">
<h4>Select Members</h4>
<div>${membersToSelect}</div>
<button id="confirmSelectionBtn">Confirm</button>
</div>
`;
// Append the modal to the body
document.body.insertAdjacentHTML('beforeend', modalContent);
// Add event for the confirmation button
const confirmBtn = document.getElementById('confirmSelectionBtn');
if (confirmBtn) {
confirmBtn.addEventListener('click', function() {
// Retrieve selected members
const selectedCheckboxes = document.querySelectorAll('.modal input[type="checkbox"]:checked');
selectedCheckboxes.forEach((checkbox: Element) => {
selectedMembers.push((checkbox as HTMLInputElement).value);
});
// Add selected members to the list
const membersList = document.getElementById('members-list');
if (membersList) {
selectedMembers.forEach(memberId => {
const member = allMembers.find(m => m.id === memberId);
if (member) {
membersList.insertAdjacentHTML('beforeend', `<div>${member.name} (${member.roleName})</div>`);
}
});
}
// Close the modal
document.querySelector('.modal')?.remove();
});
}
});
}
// Fonction d'initialisation pour le router
export function initSignature() {
// Réinitialiser l'affichage
const groupList = document.getElementById('group-list');
if (groupList) {
groupList.innerHTML = ''; // Nettoyer la liste existante
}
// Recharger les messages depuis le store
messagesMock = messageStore.getMessages();
if (messagesMock.length === 0) {
messageStore.setMessages(initialMessagesMock);
messagesMock = messageStore.getMessages();
}
// Réinitialiser l'interface
updateCurrentUserDisplay();
loadGroupList();
// Si un membre était sélectionné, recharger son chat
if (selectedMemberId) {
loadMemberChat(selectedMemberId);
}
// Event listeners
document.addEventListener('click', (event) => {
const userList = document.getElementById('userList');
const userSwitchBtn = document.getElementById('userSwitchBtn');
if (userSwitchBtn && userList && !userSwitchBtn.contains(event.target as Node) && !userList.contains(event.target as Node)) {
userList.classList.remove('show');
}
});
// Réattacher les event listeners pour les messages
const sendBtn = document.getElementById('send-button');
if (sendBtn) sendBtn.onclick = sendMessage;
const messageInput = document.getElementById('message-input');
if (messageInput) {
messageInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
sendMessage();
}
});
}
}