-
-
Description
-
${displayGroup.description || 'No description available'}
-
-
-
Documents Communs
-
- ${displayGroup.commonDocuments
- .map((document: any) => {
- const totalSignatures = document.signatures?.length || 0;
- const signedCount = document.signatures?.filter((sig: DocumentSignature) => sig.signed).length || 0;
- const percentage = totalSignatures > 0 ? (signedCount / totalSignatures) * 100 : 0;
- const isVierge = !document.createdAt || !document.deadline || !document.signatures?.length;
- const canSign = document.signatures?.some((sig: DocumentSignature) => sig.member && 'id' in sig.member && sig.member.id === currentUser.id && !sig.signed);
-
- const signButton = !isVierge
- ? `
- ${
- totalSignatures > 0 && signedCount < totalSignatures && canSign
- ? `
-
- `
- : ''
- }
- `
- : '';
-
- return `
-
-
-
- ${
- !isVierge
- ? `
-
Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}
-
Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}
-
Duration: ${this.calculateDuration(document.createdAt || '', document.deadline || '')} days
-
-
Signatures:
-
- ${document.signatures
- ?.map(
- (sig: DocumentSignature) => `
-
- ${sig.member.name}
-
- ${sig.signed ? `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'unknown date'}` : '⌛ Pending'}
-
-
- `,
- )
- .join('')}
-
-
-
${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)
-
- `
- : `
-
Document vierge - Waiting for creation
-
- `
- }
- ${signButton}
-
-
- `;
- })
- .join('')}
-
-
-
-
Roles and Documents
- ${displayGroup.roles
- .map((role: { name: string; documents?: any[] }) => {
- // Filter the documents according to the access rights
- const accessibleDocuments = (role.documents || []).filter((doc) => this.canUserAccessDocument(doc, role.name, currentUser.processRoles?.[0]?.role || ''));
-
- return `
-
-
${role.name}
-
- ${accessibleDocuments
- .map((document) => {
- const isVierge = !document.createdAt || !document.deadline || document.signatures.length === 0;
-
- const canSign = this.canUserSignDocument(document, role.name, currentUser);
-
- const signButton = !isVierge
- ? `
- ${
- document.signatures.length > 0 && document.signatures.filter((sig: DocumentSignature) => sig.signed).length < document.signatures.length && canSign
- ? `
-
- `
- : ''
- }
- `
- : '';
-
- return `
-
-
-
- ${
- !isVierge
- ? `
-
Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}
-
Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}
-
Duration: ${this.calculateDuration(document.createdAt || '', document.deadline || '')} days
- `
- : '
Document vierge - En attente de création
'
- }
-
- ${
- !isVierge
- ? `
-
-
Signatures:
-
- ${document.signatures
- .map(
- (sig: DocumentSignature) => `
-
- ${sig.member.name}
-
- ${sig.signed ? `✓ Signé le ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'date inconnue'}` : '⌛ En attente'}
-
-
- `,
- )
- .join('')}
-
-
-
${document.signatures.filter((sig: DocumentSignature) => sig.signed).length} out of ${document.signatures.length} signed (${((document.signatures.filter((sig: DocumentSignature) => sig.signed).length / document.signatures.length) * 100).toFixed(0)}%)
-
- `
- : ''
- }
- ${signButton}
-
- `;
- })
- .join('')}
-
-
- `;
- })
- .join('')}
-
-
-
Members by Role
-
- ${displayGroup.roles
- .map(
- (role: { name: string; members: Array<{ id: string | number; name: string }> }) => `
-
-
${role.name}
-
- ${role.members
- .map(
- (member) => `
- - ${member.name}
- `,
- )
- .join('')}
-
-
- `,
- )
- .join('')}
-
-
- `;
-
- const newCloseProcessButton = document.createElement('button');
- newCloseProcessButton.className = 'close-btn';
- newCloseProcessButton.textContent = 'x';
- newCloseProcessButton.addEventListener('click', () => this.closeProcessDetails(groupId));
-
- const headerButtons = detailsArea.querySelector('.header-buttons');
- if (headerButtons) {
- headerButtons.appendChild(newCloseProcessButton);
- }
- }
- }
-
- // 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,
- };
- }
- });
- });
-
- 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 = `
${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.innerHTML = '📁';
- 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);
- this.showRoleDocuments(role, group);
- } 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 = document.getElementById('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);
- });
-
- // Add the ⚙️ icon
- const settingsIcon = document.createElement('span');
- settingsIcon.textContent = '⚙️';
- settingsIcon.className = 'settings-icon';
- settingsIcon.id = `settings-${group.id}`;
-
- settingsIcon.onclick = (event) => {
- event.stopPropagation();
- this.showProcessDetails(group, group.id);
- };
-
- // Assemble the elements
- container.appendChild(nameSpan);
- container.appendChild(settingsIcon);
- 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) => `
-
-
${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);
- }
-
- // Managing the sent file
- private fileList: HTMLDivElement = document.getElementById('fileList') as HTMLDivElement;
- private getFileList() {
- const files = Array.from(this.fileList?.querySelectorAll('.file-item') || []).map((fileItem: Element) => {
- const fileName = fileItem.querySelector('.file-name')?.textContent || '';
- return {
- name: fileName,
- url: (fileItem as HTMLElement).dataset.content || '#',
- };
- });
- return files;
- }
-
- // New function to display the documents of a role
- private 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;
- files?: Array<{ name: string; url: string }>;
- }>;
- id?: number;
- },
- group: Group,
- ) {
- // Load the data from localStorage
- const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]');
- const storedGroup = storedGroups.find((g: Group) => g.id === group.id);
- const storedRole = storedGroup?.roles.find((r: any) => r.name === role.name);
-
- // Use the data from localStorage if available, otherwise use the data passed as a parameter
- const displayRole = storedRole || role;
-
- console.log('Showing documents for role:', displayRole.name, 'in group:', group.name);
- // Close all existing document views first
- const allDetailsAreas = document.querySelectorAll('.process-details');
- allDetailsAreas.forEach((area) => {
- area.remove();
- });
-
- const container = document.querySelector('.container');
- if (!container) {
- console.error('Container not found');
- return;
- }
-
- // Create a new details area
- const detailsArea = document.createElement('div');
- detailsArea.id = `role-documents-${displayRole.name}`;
- detailsArea.className = 'process-details';
- // Filter the accessible documents
- const accessibleDocuments = (displayRole.documents || []).filter((doc: { name: string; visibility: string; createdAt: string | null | undefined; deadline: string | null | undefined; signatures: DocumentSignature[]; id: number; description?: string; status?: string }) =>
- this.canUserAccessDocument(doc, displayRole.name, currentUser.processRoles?.[0]?.role || ''),
- );
-
- detailsArea.innerHTML = `
-
-
-
-
-
Documents
-
- ${accessibleDocuments
- .map((document: { name: string; visibility: string; createdAt: string | null | undefined; deadline: string | null | undefined; signatures: DocumentSignature[]; id: number; description?: string; status?: string }) => {
- 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;
-
- const canSign = this.canUserSignDocument(document, role.name, currentUser);
-
- return `
-
-
-
- ${
- !isVierge
- ? `
-
Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}
-
Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}
-
Duration: ${this.calculateDuration(document.createdAt, document.deadline)} days
-
-
Signatures:
-
- ${document.signatures
- .map(
- (sig: DocumentSignature) => `
-
- ${sig.member.name}
-
- ${sig.signed ? `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'unknown date'}` : '⌛ Pending'}
-
-
- `,
- )
- .join('')}
-
-
-
${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)
-
- `
- : `
-
Blank document - Waiting for creation
- ${
- this.canUserAccessDocument(document, displayRole.name, currentUser.processRoles?.[0]?.role || '')
- ? `
-
- `
- : ''
- }
- `
- }
-
-
- `;
- })
- .join('')}
-
-
-
-
- `;
-
- container.appendChild(detailsArea);
- }
-
- // Function to close the documents view of a role
- private closeRoleDocuments(roleName: string) {
- const detailsArea = document.getElementById(`role-documents-${roleName}`);
- if (detailsArea) {
- detailsArea.remove();
- }
- }
-
- private handleFiles(files: FileList, fileList: HTMLDivElement) {
- Array.from(files).forEach((file) => {
- const reader = new FileReader();
- reader.onload = (e) => {
- const fileContent = e.target?.result;
- 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 = `
-
- ${file.name}
- (${(file.size / 1024).toFixed(1)} KB)
-
-
- `;
- fileItem.dataset.content = fileContent as string;
-
- const removeBtn = fileItem.querySelector('.remove-file');
- if (removeBtn) {
- removeBtn.addEventListener('click', () => fileItem.remove());
- }
-
- fileList.appendChild(fileItem);
- }
- };
- reader.readAsDataURL(file);
- });
- }
-
- // Function to manage the new request
- private newRequest(params: RequestParams) {
- // Add parameter validation
- if (!params || !params.processId) {
- console.error('Paramètres invalides:', params);
- showAlert('Invalid parameters for new request');
- return;
- }
-
- const modal = document.createElement('div');
- modal.className = 'modal-overlay';
-
- // Retrieve the process with a verification
- const process = groupsMock.find((g) => g.id === params.processId);
- if (!process) {
- console.error('Processus non trouvé:', params.processId);
- showAlert('Process not found');
- return;
- }
-
- // Determine the members with an additional verification
- let membersToDisplay = [];
- try {
- if (params.roleName === 'common') {
- membersToDisplay = process.roles.reduce((members: any[], role) => {
- return members.concat(
- role.members.map((member) => ({
- ...member,
- roleName: role.name,
- })),
- );
- }, []);
- } else {
- const role = process.roles.find((r) => r.name === params.roleName);
- if (!role) {
- throw new Error(`Role ${params.roleName} not found`);
- }
- membersToDisplay = role.members.map((member) => ({
- ...member,
- roleName: params.roleName,
- }));
- }
- } catch (error) {
- console.error('Error retrieving members:', error);
- showAlert('Error retrieving members');
- return;
- }
-
- modal.innerHTML = `
-
- `;
-
- 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;
-
- // Make the area clickable
- dropZone.addEventListener('click', () => {
- fileInput.click();
- });
-
- // Manage the file selection
- fileInput.addEventListener('change', (e: Event) => {
- const target = e.target as HTMLInputElement;
- if (target.files && target.files.length > 0) {
- this.handleFiles(target.files, fileList);
- }
- });
-
- // Manage the 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) {
- this.handleFiles(e.dataTransfer.files, fileList);
- }
- });
- }
-
- private closeModal(button: HTMLElement) {
- const modalOverlay = button.closest('.modal-overlay');
- if (modalOverlay) {
- modalOverlay.remove();
- }
- }
-
- private submitNewDocument(event: Event) {
- event.preventDefault();
-
- const form = document.getElementById('newDocumentForm') as HTMLFormElement;
- if (!form) {
- showAlert('Form not found');
- return;
- }
-
- // Retrieve the files
- const fileList = document.getElementById('fileList') as HTMLDivElement;
- const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map((fileItem) => {
- const fileName = fileItem.querySelector('.file-name')?.textContent || '';
- return {
- name: fileName,
- url: (fileItem as HTMLElement).dataset.content || '#',
- };
- });
-
- // Retrieve the values from the form
- 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;
- }
-
- try {
- // Retrieve the current data
- const 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;
- }
-
- const role = group.roles.find((r: any) => r.documents?.some((d: any) => d.id === documentId));
-
- if (!role) {
- showAlert('Role not found');
- return;
- }
-
- // Create the new document with the signatures of the role members
- 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,
- })),
- files: files, // Ajout des fichiers au document
- };
-
- // Update the document in the role
- const documentIndex = role.documents.findIndex((d: any) => d.id === documentId);
- if (documentIndex !== -1) {
- role.documents[documentIndex] = updatedDocument;
- }
-
- // Save in localStorage
- localStorage.setItem('groups', JSON.stringify(groups));
-
- // Also update groupsMock for consistency
- const mockGroup = groupsMock.find((g) => g.id === processId);
- if (mockGroup) {
- const mockRole = mockGroup?.roles.find((r) => r.name === role.name);
- if (mockRole?.documents) {
- const mockDocIndex = mockRole.documents.findIndex((d) => d.id === documentId);
- if (mockDocIndex !== -1) {
- mockRole.documents[mockDocIndex] = {
- ...updatedDocument,
- status: undefined,
- };
- }
- }
- }
-
- // Close the modal
- if (event.target instanceof HTMLElement) {
- this.closeModal(event.target);
- }
-
- // Reload the documents view with the updated data
- this.showRoleDocuments(role, group);
- showAlert('Document updated successfully!');
- } catch (error) {
- console.error('Error saving:', error);
- showAlert('An error occurred while saving');
- }
- }
-
- private submitCommonDocument(event: Event) {
- event.preventDefault();
-
- const form = document.getElementById('newDocumentForm') as HTMLFormElement;
- if (!form) {
- showAlert('Form not found');
- return;
- }
-
- 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;
-
- if (!documentName || !description || !deadline) {
- showAlert('Please fill in all required fields');
- return;
- }
-
- try {
- const 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;
- }
-
- // Retrieve all members of all roles in the group
- const allMembers = group.roles.reduce((acc: any[], role: any) => {
- return acc.concat(role.members);
- }, []);
-
- const fileList = document.getElementById('fileList') as HTMLDivElement;
- const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map((fileItem) => {
- const fileName = fileItem.querySelector('.file-name')?.textContent || '';
- return {
- name: fileName,
- url: (fileItem as HTMLElement).dataset.content || '#',
- };
- });
-
- const updatedDocument = {
- id: documentId,
- name: documentName,
- description: description,
- createdAt: new Date().toISOString(),
- deadline: deadline,
- visibility: visibility,
- status: 'pending',
- signatures: allMembers.map((member: { id: string | number; name: string }) => ({
- member: member,
- signed: false,
- signedAt: null,
- })),
- files: files,
- };
-
- // Update the common document
- const documentIndex = group.commonDocuments.findIndex((d: { id: number }) => d.id === documentId);
- if (documentIndex !== -1) {
- group.commonDocuments[documentIndex] = updatedDocument;
- }
-
- localStorage.setItem('groups', JSON.stringify(groups));
-
- if (event.target instanceof HTMLElement) {
- this.closeModal(event.target);
- }
-
- this.showProcessDetails(group, group.id);
- showAlert('Document common updated successfully!');
- } catch (error) {
- console.error('Error saving:', error);
- showAlert('An error occurred while saving');
- }
- }
-
- private submitRequest() {
- showAlert('Request submitted!');
- }
-
- private closeNewRequest() {
- const newRequestView = document.getElementById('new-request-view');
- if (newRequestView) {
- newRequestView.style.display = 'none';
- newRequestView.remove();
- }
- }
-
- private 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!');
- this.closeNewRequest();
- }
-
- // FUNCTIONS FOR SIGNATURE
-
- // New function to confirm the signature
- private confirmSignature(documentId: number, processId: number, isCommonDocument: boolean) {
- try {
- // Add console.log to see the current user
- console.log('Current user:', currentUser);
-
- const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock));
- const group = groups.find((g: Group) => g.id === processId);
-
- if (!group) {
- throw new Error('Process not found');
- }
-
- let targetDoc;
- if (isCommonDocument) {
- targetDoc = group.commonDocuments.find((d: any) => d.id === documentId);
- } else {
- for (const role of group.roles) {
- if (role.documents) {
- targetDoc = role.documents.find((d: any) => d.id === documentId);
- if (targetDoc) break;
- }
- }
- }
-
- if (!targetDoc) {
- throw new Error('Document not found');
- }
-
- const userSignature = targetDoc.signatures.find((sig: DocumentSignature) => sig.member.name === currentUser.name);
-
- if (!userSignature) {
- throw new Error(`The user ${currentUser.name} is not authorized to sign this document. Please log in with an authorized user.`);
- }
-
- userSignature.signed = true;
- userSignature.signedAt = new Date().toISOString();
-
- localStorage.setItem('groups', JSON.stringify(groups));
-
- const closeBtn = document.querySelector('.modal-overlay .close-btn');
- if (closeBtn instanceof HTMLElement) {
- this.closeModal(closeBtn);
- }
-
- if (isCommonDocument) {
- this.showProcessDetails(group, processId);
- } else {
- const role = group.roles.find((r: any) => r.documents?.includes(targetDoc));
- if (role) {
- this.showRoleDocuments(role, group);
- }
- }
-
- showAlert('Document signed successfully!');
- } catch (error) {
- console.error('Error signing document:', error);
- showAlert(error instanceof Error ? error.message : 'Error signing document');
- }
- }
-
- private initializeEventListeners() {
- document.addEventListener('DOMContentLoaded', (): void => {
- const newRequestBtn = document.getElementById('newRequestBtn');
- if (newRequestBtn) {
- newRequestBtn.addEventListener('click', (): void => {
- this.newRequest({
- processId: 0,
- processName: '',
- roleId: 0,
- roleName: '',
- documentId: 0,
- documentName: '',
- });
- });
- }
- });
-
- // 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() {
- if (this.shadowRoot) {
- this.shadowRoot.innerHTML = `
-
- `;
- }
-
- this.messagesMock = messageStore.getMessages();
- if (this.messagesMock.length === 0) {
- messageStore.setMessages(initialMessagesMock);
- this.messagesMock = messageStore.getMessages();
- }
- this.updateCurrentUserDisplay();
- this.initializeEventListeners();
- this.loadGroupList();
- }
-}
-
-customElements.define('signature-element', SignatureElement);
-export { SignatureElement };
+import signatureStyle from '../../../public/style/signature.css?inline';
+
+declare global {
+ interface Window {
+ toggleUserList: () => void;
+ switchUser: (userId: string | number) => void;
+ closeProcessDetails: (groupId: number) => void;
+ loadMemberChat: (memberId: string | number) => void;
+ closeRoleDocuments: (roleName: string) => void;
+ newRequest: (params: RequestParams) => void;
+ submitRequest: () => void;
+ closeNewRequest: () => void;
+ closeModal: (button: HTMLElement) => void;
+ submitDocumentRequest: (documentId: number) => void;
+ submitNewDocument: (event: Event) => void;
+ submitCommonDocument: (event: Event) => void;
+ signDocument: (documentId: number, processId: number, isCommonDocument: boolean) => void;
+ confirmSignature: (documentId: number, processId: number, isCommonDocument: boolean) => 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,
+ RequestParams} 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';
+
+
+let currentUser: Member = membersMock[0];
+
+interface LocalNotification {
+ memberId: string;
+ text: string;
+ time: string;
+}
+
+export function showAlert(message: string): void {
+ // Créer la popup si elle n'existe pas
+ let alertPopup = document.querySelector('.alert-popup');
+ if (!alertPopup) {
+ alertPopup = document.createElement('div');
+ alertPopup.className = 'alert-popup';
+ document.body.appendChild(alertPopup);
+ }
+
+ // Définir le message et afficher la popup
+ alertPopup.textContent = message;
+ (alertPopup as HTMLElement).style.display = 'block';
+
+ // Cacher la popup après 3 secondes
+ setTimeout(() => {
+ (alertPopup as HTMLElement).style.display = 'none';
+ }, 3000);
+}
+
+
+class SignatureElement extends HTMLElement {
+ 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'
+ }));
+
+ private signDocument(documentId: number, processId: number, isCommonDocument: boolean = false): void {
+ try {
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
+ console.error('Cette fonction ne peut être exécutée que dans un navigateur');
+ return;
+ }
+
+ const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock));
+ const group = groups.find((g: Group) => g.id === processId);
+
+ if (!group) {
+ throw new Error('Process not found');
+ }
+
+ let targetDoc;
+ if (isCommonDocument) {
+ targetDoc = group.commonDocuments.find((d: any) => d.id === documentId);
+ } else {
+ for (const role of group.roles) {
+ if (role.documents) {
+ targetDoc = role.documents.find((d: any) => d.id === documentId);
+ if (targetDoc) break;
+ }
+ }
+ }
+
+ if (!targetDoc) {
+ throw new Error('Document not found');
+ }
+
+ const canSign = isCommonDocument ?
+ targetDoc.signatures?.some((sig: DocumentSignature) =>
+ sig.member?.name === currentUser?.name && !sig.signed
+ ) :
+ this.canUserSignDocument(targetDoc, currentUser?.name, currentUser);
+
+ if (!canSign) {
+ showAlert("You do not have the necessary rights to sign this document.");
+ return;
+ }
+
+ // Create and insert the modal directly into the body
+ const modalHtml = `
+
+
+
+
+
+
+
${targetDoc.name}
+
+
+
+ Created:
+ ${new Date(targetDoc.createdAt).toLocaleDateString()}
+
+
+ Deadline:
+ ${new Date(targetDoc.deadline).toLocaleDateString()}
+
+
+ Visibility:
+ ${targetDoc.visibility}
+
+
+
+
+
Description:
+
${targetDoc.description || 'No description available'}
+
+
+
+
Signatures status:
+
+ ${targetDoc.signatures.map((sig: DocumentSignature) => `
+
+ ${sig.member.name}
+
+ ${sig.signed ?
+ `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'unknown date'}` :
+ '⌛ Pending'}
+
+
+ `).join('')}
+
+
+
+ ${this.getFileList().length > 0 ? `
+
+
Files attached:
+
+ ${this.getFileList().map(file => `
+
+
+
${file.name}
+
+ ⬇
+
+
+ `).join('')}
+
+
+ ` : ''}
+
+
+
By signing this document, you confirm that you have read its contents.
+
+
+
+
+
+
`;
+
+ // Créer la modal dans le shadowDOM au lieu du document principal
+ const modalElement = document.createElement('div');
+ modalElement.className = 'modal-overlay';
+ modalElement.innerHTML = modalHtml;
+ this.shadowRoot?.appendChild(modalElement);
+
+ // Sélectionner les éléments dans le shadowDOM
+ const slider = modalElement.querySelector('#signatureSlider');
+ const sliderTrack = slider?.parentElement;
+ let isDragging = false;
+ let startX: number;
+ let sliderLeft: number;
+
+ if (slider && sliderTrack) {
+ slider.addEventListener('mousedown', (e: Event) => initDrag(e as MouseEvent));
+ slider.addEventListener('touchstart', (e: Event) => initDrag(e as TouchEvent));
+
+ modalElement.addEventListener('mousemove', (e: Event) => drag.call(this, e as MouseEvent));
+ modalElement.addEventListener('touchmove', (e: Event) => drag.call(this, e as TouchEvent));
+ modalElement.addEventListener('mouseup', (e: Event) => stopDrag(e as MouseEvent));
+ modalElement.addEventListener('touchend', (e: Event) => stopDrag(e as TouchEvent));
+ }
+
+ function initDrag(e: MouseEvent | TouchEvent) {
+ isDragging = true;
+ startX = 'touches' in e ? e.touches[0].clientX : e.clientX;
+ sliderLeft = (slider as HTMLElement)?.offsetLeft || 0;
+ }
+
+ function drag(this: SignatureElement, e: MouseEvent | TouchEvent) {
+ if (!isDragging || !slider || !sliderTrack) return;
+
+ e.preventDefault();
+ const x = 'touches' in e ? e.touches[0].clientX : e.clientX;
+ const deltaX = x - startX;
+
+ // Calculate the position relative to the track
+ let newLeft = sliderLeft + deltaX;
+
+ // Limit the movement
+ const maxLeft = (sliderTrack as HTMLElement).offsetWidth - (slider as HTMLElement).offsetWidth;
+ newLeft = Math.max(0, Math.min(newLeft, maxLeft));
+
+ // Update the position
+ (slider as HTMLElement).style.left = `${newLeft}px`;
+
+ // If the slider reaches 90% of the path, trigger the signature
+ if (newLeft > maxLeft * 0.9) {
+ stopDrag(e);
+ this.confirmSignature(documentId, processId, isCommonDocument);
+ }
+ }
+
+ function stopDrag(e: MouseEvent | TouchEvent) {
+ if (!isDragging || !slider) return;
+ isDragging = false;
+
+ // Reset the position if not enough dragged
+ if ((slider as HTMLElement).offsetLeft < ((sliderTrack as HTMLElement)?.offsetWidth || 0) * 0.9) {
+ (slider as HTMLElement).style.left = '0px';
+ }
+ }
+
+ } catch (error) {
+ console.error('Error displaying modal:', error);
+ showAlert(error instanceof Error ? error.message : 'Error displaying modal');
+ }
+ }
+
+ constructor() {
+ super();
+ this.attachShadow({ mode: 'open' });
+ this.dom = getCorrectDOM('signature-element');
+
+ this.shadowRoot!.innerHTML = `
+
+
+ `;
+
+ window.toggleUserList = this.toggleUserList.bind(this);
+ window.switchUser = this.switchUser.bind(this);
+ window.closeProcessDetails = this.closeProcessDetails.bind(this);
+ window.loadMemberChat = this.loadMemberChat.bind(this);
+ window.closeRoleDocuments = this.closeRoleDocuments.bind(this);
+ window.newRequest = this.newRequest.bind(this);
+ window.submitRequest = this.submitRequest.bind(this);
+ window.closeNewRequest = this.closeNewRequest.bind(this);
+ window.closeModal = this.closeModal.bind(this);
+ window.submitNewDocument = this.submitNewDocument.bind(this);
+ window.submitCommonDocument = this.submitCommonDocument.bind(this);
+ window.signDocument = this.signDocument.bind(this);
+ window.confirmSignature = this.confirmSignature.bind(this);
+ window.submitDocumentRequest = this.submitDocumentRequest.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();
+ }
+
+ private initMessageEvents() {
+ // Pour le bouton Send
+ const sendButton = this.shadowRoot?.getElementById('send-button');
+ if (sendButton) {
+ sendButton.addEventListener('click', () => this.sendMessage());
+ }
+
+ // Pour la touche Entrée
+ const messageInput = this.shadowRoot?.getElementById('message-input');
+ if (messageInput) {
+ messageInput.addEventListener('keypress', (event: KeyboardEvent) => {
+ if (event.key === 'Enter' && !event.shiftKey) {
+ event.preventDefault();
+ this.sendMessage();
+ }
+ });
+ }
+ }
+
+ private initFileUpload() {
+ const fileInput = this.shadowRoot?.getElementById('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]);
+ }
+ });
+ }
+ }
+
+
+ private 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));
+ }
+
+ // Add this helper function
+ private canUserAccessDocument(document: any, roleId: string, currentUserRole: string): boolean {
+ // Modify the access logic
+ if (document.visibility === 'public') {
+ return true; // Can see but not necessarily sign
+ }
+ return roleId === currentUserRole;
+ }
+
+ private canUserSignDocument(document: any, role: string, user: Member): boolean {
+ console.log('Checking signing rights for:', {
+ document,
+ role,
+ user,
+ userRoles: user.processRoles
+ });
+
+ // Vérifier si l'utilisateur est dans la liste des signatures
+ const isSignatory = document.signatures?.some((sig: DocumentSignature) =>
+ sig.member && 'id' in sig.member && sig.member.id === user.id && !sig.signed
+ );
+
+ if (!isSignatory) {
+ console.log('User is not in signatures list or has already signed');
+ return false;
+ }
+
+ // Si l'utilisateur est dans la liste des signatures, il peut signer
+ return true;
+ }
+
+ private closeProcessDetails(groupId: number) {
+ const detailsArea = this.shadowRoot?.getElementById(`process-details-${groupId}`);
+ const chatArea = this.shadowRoot?.getElementById('chat-area');
+
+ if (detailsArea) {
+ detailsArea.style.display = 'none';
+ }
+
+ if (chatArea) {
+ chatArea.style.display = 'block';
+ }
+ }
+
+ ///////////////////// 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?.getElementById('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);
+}
+
+
+ private showProcessDetails(group: Group, groupId: number) {
+ console.log('Showing details for group:', groupId);
+
+ // Close all existing process views
+ const allDetailsAreas = this.shadowRoot?.querySelectorAll('.process-details');
+ if (allDetailsAreas) {
+ allDetailsAreas.forEach(area => {
+ (area as HTMLElement).style.display = 'none';
+ });
+ }
+
+ const container = this.shadowRoot?.querySelector('.container');
+ if (!container) {
+ console.error('Container not found');
+ return;
+ }
+
+ // Load the data from localStorage
+ const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]');
+ const storedGroup = storedGroups.find((g: Group) => g.id === groupId);
+
+ // Use the data from localStorage if available, otherwise use the group passed as a parameter
+ const displayGroup = storedGroup || group;
+
+ let detailsArea = this.shadowRoot?.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 = `
+
+
+
+
Description
+
${displayGroup.description || 'No description available'}
+
+
+
Documents Communs
+
+ ${displayGroup.commonDocuments.map((document: any) => {
+ const totalSignatures = document.signatures?.length || 0;
+ const signedCount = document.signatures?.filter((sig: DocumentSignature) => sig.signed).length || 0;
+ const percentage = totalSignatures > 0 ? (signedCount / totalSignatures) * 100 : 0;
+ const isVierge = !document.createdAt || !document.deadline || !document.signatures?.length;
+ const canSign = document.signatures?.some((sig: DocumentSignature) =>
+ sig.member && 'id' in sig.member && sig.member.id === currentUser.id && !sig.signed
+ );
+
+ const signButton = !isVierge ? `
+ ${totalSignatures > 0 && signedCount < totalSignatures && canSign ? `
+
+ ` : ''}
+ ` : '';
+
+ return `
+
+
+
+ ${!isVierge ? `
+
Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}
+
Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}
+
Duration: ${this.calculateDuration(document.createdAt || '', document.deadline || '')} days
+
+
Signatures:
+
+ ${document.signatures?.map((sig: DocumentSignature) => `
+
+ ${sig.member.name}
+
+ ${sig.signed ?
+ `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'unknown date'}` :
+ '⌛ Pending'}
+
+
+ `).join('')}
+
+
+
${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)
+
+ ` : `
+
Document vierge - Waiting for creation
+
+ `}
+ ${signButton}
+
+
+ `;
+ }).join('')}
+
+
+
+
Roles and Documents
+ ${displayGroup.roles.map((role: { name: string; documents?: any[] }) => {
+ // Filter the documents according to the access rights
+ const accessibleDocuments = (role.documents || []).filter(doc =>
+ this.canUserAccessDocument(doc, role.name, currentUser.processRoles?.[0]?.role || '')
+ );
+
+ return `
+
+
${role.name}
+
+ ${accessibleDocuments.map(document => {
+ const isVierge = !document.createdAt ||
+ !document.deadline ||
+ document.signatures.length === 0;
+
+ const canSign = this.canUserSignDocument(document, role.name, currentUser);
+
+ const signButton = !isVierge ? `
+ ${document.signatures.length > 0 &&
+ document.signatures.filter((sig: DocumentSignature) => sig.signed).length < document.signatures.length &&
+ canSign ? `
+
+ ` : ''}
+ ` : '';
+
+ return `
+
+
+
+ ${!isVierge ? `
+
Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}
+
Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}
+
Duration: ${this.calculateDuration(document.createdAt || '', document.deadline || '')} days
+ ` : '
Document vierge - Waiting for creation
'}
+
+ ${!isVierge ? `
+
+
Signatures:
+
+ ${document.signatures.map((sig: DocumentSignature) => `
+
+ ${sig.member.name}
+
+ ${sig.signed ?
+ `✓ Signé le ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'date inconnue'}` :
+ '⌛ En attente'}
+
+
+ `).join('')}
+
+
+
${document.signatures.filter((sig: DocumentSignature) => sig.signed).length} out of ${document.signatures.length} signed (${(document.signatures.filter((sig: DocumentSignature) => sig.signed).length / document.signatures.length * 100).toFixed(0)}%)
+
+ ` : ''}
+ ${signButton}
+
+ `;
+ }).join('')}
+
+
+ `;
+ }).join('')}
+
+
+
Members by Role
+
+ ${displayGroup.roles.map((role: { name: string; members: Array<{ id: string | number; name: string }> }) => `
+
+
${role.name}
+
+ ${role.members.map(member => `
+ - ${member.name}
+ `).join('')}
+
+
+ `).join('')}
+
+
+ `;
+
+
+ const newCloseProcessButton = document.createElement('button');
+ newCloseProcessButton.className = 'close-btn';
+ newCloseProcessButton.textContent = 'x';
+ newCloseProcessButton.addEventListener('click', () => this.closeProcessDetails(groupId));
+
+ const headerButtons = detailsArea.querySelector('.header-buttons');
+ if (headerButtons) {
+ headerButtons.appendChild(newCloseProcessButton);
+ }
+ }
+ }
+
+ // 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
+ };
+ }
+ });
+ });
+
+ const chatHeader = this.shadowRoot?.getElementById('chat-header');
+ const messagesContainer = this.shadowRoot?.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 = `
${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.innerHTML = '📁';
+ 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);
+ this.showRoleDocuments(role, group);
+ } 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?.getElementById('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);
+ });
+
+ // Add the ⚙️ icon
+ const settingsIcon = document.createElement('span');
+ settingsIcon.textContent = '⚙️';
+ settingsIcon.className = 'settings-icon';
+ settingsIcon.id = `settings-${group.id}`;
+
+ settingsIcon.onclick = (event) => {
+ event.stopPropagation();
+ this.showProcessDetails(group, group.id);
+ };
+
+ // Assemble the elements
+ container.appendChild(nameSpan);
+ container.appendChild(settingsIcon);
+ 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 => `
+
+
${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);
+ }
+
+ // Managing the sent file
+ private fileList: HTMLDivElement = this.shadowRoot?.getElementById('fileList') as HTMLDivElement;
+ private getFileList() {
+ const files = Array.from(this.fileList?.querySelectorAll('.file-item') || []).map((fileItem: Element) => {
+ const fileName = fileItem.querySelector('.file-name')?.textContent || '';
+ return {
+ name: fileName,
+ url: (fileItem as HTMLElement).dataset.content || '#',
+ };
+ });
+ return files;
+ }
+
+ // New function to display the documents of a role
+ private 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;
+ files?: Array<{ name: string; url: string }>;
+ }>;
+ id?: number;
+ }, group: Group) {
+ // Load the data from localStorage
+ const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]');
+ const storedGroup = storedGroups.find((g: Group) => g.id === group.id);
+ const storedRole = storedGroup?.roles.find((r: any) => r.name === role.name);
+
+ // Use the data from localStorage if available, otherwise use the data passed as a parameter
+ const displayRole = storedRole || role;
+
+ console.log('Showing documents for role:', displayRole.name, 'in group:', group.name);
+ // Close all existing document views first
+ const allDetailsAreas = this.shadowRoot?.querySelectorAll('.process-details');
+ allDetailsAreas?.forEach(area => {
+ area.remove();
+ });
+
+ const container = this.shadowRoot?.querySelector('.container');
+ if (!container) {
+ console.error('Container not found');
+ return;
+ }
+
+ // Create a new details area
+ const detailsArea = document.createElement('div');
+ detailsArea.id = `role-documents-${displayRole.name}`;
+ detailsArea.className = 'process-details';
+ // Filter the accessible documents
+ const accessibleDocuments = (displayRole.documents || []).filter((doc: {
+ name: string;
+ visibility: string;
+ createdAt: string | null | undefined;
+ deadline: string | null | undefined;
+ signatures: DocumentSignature[];
+ id: number;
+ description?: string;
+ status?: string;
+ }) =>
+ this.canUserAccessDocument(doc, displayRole.name, currentUser.processRoles?.[0]?.role || '')
+ );
+
+ detailsArea.innerHTML = `
+
+
+
+
+
Documents
+
+ ${accessibleDocuments.map((document: {
+ name: string;
+ visibility: string;
+ createdAt: string | null | undefined;
+ deadline: string | null | undefined;
+ signatures: DocumentSignature[];
+ id: number;
+ description?: string;
+ status?: string;
+ }) => {
+ 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;
+
+ const canSign = this.canUserSignDocument(document, role.name, currentUser);
+
+ return `
+
+
+
+ ${!isVierge ? `
+
Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}
+
Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}
+
Duration: ${this.calculateDuration(document.createdAt, document.deadline)} days
+
+
Signatures:
+
+ ${document.signatures.map((sig: DocumentSignature) => `
+
+ ${sig.member.name}
+
+ ${sig.signed ?
+ `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'unknown date'}` :
+ '⌛ Pending'}
+
+
+ `).join('')}
+
+
+
${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)
+
+ ` : `
+
Blank document - Waiting for creation
+ ${this.canUserAccessDocument(document, displayRole.name, currentUser.processRoles?.[0]?.role || '') ? `
+
+ ` : ''}
+ `}
+
+
+ `;
+ }).join('')}
+
+
+
+
+ `;
+
+ container.appendChild(detailsArea);
+ }
+
+ // Function to close the documents view of a role
+ private closeRoleDocuments(roleName: string) {
+ const detailsArea = this.shadowRoot?.getElementById(`role-documents-${roleName}`);
+ if (detailsArea) {
+
+ detailsArea.remove();
+ }
+ }
+
+ private handleFiles(files: FileList, fileList: HTMLDivElement) {
+ Array.from(files).forEach(file => {
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const fileContent = e.target?.result;
+ 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 = `
+
+ ${file.name}
+ (${(file.size / 1024).toFixed(1)} KB)
+
+
+ `;
+ fileItem.dataset.content = fileContent as string;
+
+ const removeBtn = fileItem.querySelector('.remove-file');
+ if (removeBtn) {
+ removeBtn.addEventListener('click', () => fileItem.remove());
+ }
+
+ fileList.appendChild(fileItem);
+ }
+ };
+ reader.readAsDataURL(file);
+ });
+ }
+
+ // Function to manage the new request
+ private newRequest(params: RequestParams) {
+ // Add parameter validation
+ if (!params || !params.processId) {
+ console.error('Paramètres invalides:', params);
+ showAlert('Invalid parameters for new request');
+ return;
+ }
+
+ const modal = document.createElement('div');
+ modal.className = 'modal-overlay';
+
+ // Retrieve the process with a verification
+ const process = groupsMock.find(g => g.id === params.processId);
+ if (!process) {
+ console.error('Processus non trouvé:', params.processId);
+ showAlert('Process not found');
+ return;
+ }
+
+ // Determine the members with an additional verification
+ let membersToDisplay = [];
+ try {
+ if (params.roleName === 'common') {
+ membersToDisplay = process.roles.reduce((members: any[], role) => {
+ return members.concat(role.members.map(member => ({
+ ...member,
+ roleName: role.name
+ })));
+ }, []);
+ } else {
+ const role = process.roles.find(r => r.name === params.roleName);
+ if (!role) {
+ throw new Error(`Role ${params.roleName} not found`);
+ }
+ membersToDisplay = role.members.map(member => ({
+ ...member,
+ roleName: params.roleName
+ }));
+ }
+ } catch (error) {
+ console.error('Error retrieving members:', error);
+ showAlert('Error retrieving members');
+ return;
+ }
+
+
+
+ modal.innerHTML = `
+
+ `;
+
+ 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;
+
+ // Make the area clickable
+ dropZone.addEventListener('click', () => {
+ fileInput.click();
+ });
+
+ // Manage the file selection
+ fileInput.addEventListener('change', (e: Event) => {
+ const target = e.target as HTMLInputElement;
+ if (target.files && target.files.length > 0) {
+ this.handleFiles(target.files, fileList);
+ }
+ });
+
+ // Manage the 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) {
+ this.handleFiles(e.dataTransfer.files, fileList);
+ }
+ });
+ }
+
+ private closeModal(button: HTMLElement) {
+ const modalOverlay = button.closest('.modal-overlay');
+ if (modalOverlay) {
+ modalOverlay.remove();
+ }
+ }
+
+ private submitNewDocument(event: Event) {
+ event.preventDefault();
+
+ const form = document.getElementById('newDocumentForm') as HTMLFormElement;
+ if (!form) {
+ showAlert('Form not found');
+ return;
+ }
+
+ // Retrieve the files
+ const fileList = this.shadowRoot?.getElementById('fileList') as HTMLDivElement;
+ const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map(fileItem => {
+ const fileName = fileItem.querySelector('.file-name')?.textContent || '';
+ return {
+ name: fileName,
+ url: (fileItem as HTMLElement).dataset.content || '#',
+ };
+ });
+
+ // Retrieve the values from the form
+ 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;
+ }
+
+ try {
+ // Retrieve the current data
+ const 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;
+ }
+
+ const role = group.roles.find((r: any) =>
+ r.documents?.some((d: any) => d.id === documentId)
+ );
+
+ if (!role) {
+ showAlert('Role not found');
+ return;
+ }
+
+ // Create the new document with the signatures of the role members
+ 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
+ })),
+ files: files // Ajout des fichiers au document
+ };
+
+ // Update the document in the role
+ const documentIndex = role.documents.findIndex((d: any) => d.id === documentId);
+ if (documentIndex !== -1) {
+ role.documents[documentIndex] = updatedDocument;
+ }
+
+ // Save in localStorage
+ localStorage.setItem('groups', JSON.stringify(groups));
+
+ // Also update groupsMock for consistency
+ const mockGroup = groupsMock.find(g => g.id === processId);
+ if (mockGroup) {
+ const mockRole = mockGroup?.roles.find(r => r.name === role.name);
+ if (mockRole?.documents) {
+ const mockDocIndex = mockRole.documents.findIndex(d => d.id === documentId);
+ if (mockDocIndex !== -1) {
+ mockRole.documents[mockDocIndex] = {
+ ...updatedDocument,
+ status: undefined
+ };
+ }
+ }
+ }
+
+ // Close the modal
+ if (event.target instanceof HTMLElement) {
+ this.closeModal(event.target);
+ }
+
+ // Reload the documents view with the updated data
+ this.showRoleDocuments(role, group);
+ showAlert('Document updated successfully!');
+
+ } catch (error) {
+ console.error('Error saving:', error);
+ showAlert('An error occurred while saving');
+ }
+ }
+
+ private submitCommonDocument(event: Event) {
+ event.preventDefault();
+
+ const form = document.getElementById('newDocumentForm') as HTMLFormElement;
+ if (!form) {
+ showAlert('Form not found');
+ return;
+ }
+
+ 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;
+
+ if (!documentName || !description || !deadline) {
+ showAlert('Please fill in all required fields');
+ return;
+ }
+
+ try {
+ const 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;
+ }
+
+ // Retrieve all members of all roles in the group
+ const allMembers = group.roles.reduce((acc: any[], role: any) => {
+ return acc.concat(role.members);
+ }, []);
+
+ const fileList = this.shadowRoot?.getElementById('fileList') as HTMLDivElement;
+ const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map(fileItem => {
+ const fileName = fileItem.querySelector('.file-name')?.textContent || '';
+ return {
+ name: fileName,
+ url: (fileItem as HTMLElement).dataset.content || '#',
+ };
+ });
+
+ const updatedDocument = {
+ id: documentId,
+ name: documentName,
+ description: description,
+ createdAt: new Date().toISOString(),
+ deadline: deadline,
+ visibility: visibility,
+ status: "pending",
+ signatures: allMembers.map((member: { id: string | number; name: string }) => ({
+ member: member,
+ signed: false,
+ signedAt: null
+ })),
+ files: files
+ };
+
+ // Update the common document
+ const documentIndex = group.commonDocuments.findIndex((d: { id: number }) => d.id === documentId);
+ if (documentIndex !== -1) {
+ group.commonDocuments[documentIndex] = updatedDocument;
+ }
+
+ localStorage.setItem('groups', JSON.stringify(groups));
+
+ if (event.target instanceof HTMLElement) {
+ this.closeModal(event.target);
+ }
+
+ this.showProcessDetails(group, group.id);
+ showAlert('Document common updated successfully!');
+
+ } catch (error) {
+ console.error('Error saving:', error);
+ showAlert('An error occurred while saving');
+ }
+ }
+
+
+ private submitRequest() {
+
+ showAlert("Request submitted!");
+ }
+
+ private closeNewRequest() {
+ const newRequestView = document.getElementById('new-request-view');
+ if (newRequestView) {
+ newRequestView.style.display = 'none';
+ newRequestView.remove();
+ }
+ }
+
+ private submitDocumentRequest(documentId: number) {
+ const createdAt = (this.shadowRoot?.getElementById('createdAt') as HTMLInputElement)?.value || '';
+ const deadline = (this.shadowRoot?.getElementById('deadline') as HTMLInputElement)?.value || '';
+ const visibility = (this.shadowRoot?.getElementById('visibility') as HTMLSelectElement)?.value || '';
+ const description = (this.shadowRoot?.getElementById('description') as HTMLTextAreaElement)?.value || '';
+
+ const selectedMembers = Array.from(
+ this.shadowRoot?.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!');
+ this.closeNewRequest();
+ }
+
+ // FUNCTIONS FOR SIGNATURE
+
+ // New function to confirm the signature
+ private confirmSignature(documentId: number, processId: number, isCommonDocument: boolean) {
+ try {
+ // Add console.log to see the current user
+ console.log('Current user:', currentUser);
+
+ const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock));
+ const group = groups.find((g: Group) => g.id === processId);
+
+ if (!group) {
+ throw new Error('Process not found');
+ }
+
+ let targetDoc;
+ if (isCommonDocument) {
+ targetDoc = group.commonDocuments.find((d: any) => d.id === documentId);
+ } else {
+ for (const role of group.roles) {
+ if (role.documents) {
+ targetDoc = role.documents.find((d: any) => d.id === documentId);
+ if (targetDoc) break;
+ }
+ }
+ }
+
+ if (!targetDoc) {
+ throw new Error('Document not found');
+ }
+
+ const userSignature = targetDoc.signatures.find((sig: DocumentSignature) =>
+ sig.member.name === currentUser.name
+ );
+
+ if (!userSignature) {
+ throw new Error(`The user ${currentUser.name} is not authorized to sign this document. Please log in with an authorized user.`);
+ }
+
+ // Mettre à jour la signature
+ userSignature.signed = true;
+ userSignature.signedAt = new Date().toISOString();
+ localStorage.setItem('groups', JSON.stringify(groups));
+
+ // Supprimer la modal de signature
+ const modalOverlay = this.shadowRoot?.querySelector('.modal-overlay');
+ if (modalOverlay) {
+ modalOverlay.remove();
+ }
+
+ // Rafraîchir l'affichage
+ if (isCommonDocument) {
+ this.showProcessDetails(group, processId);
+ } else {
+ const role = group.roles.find((r: any) => r.documents?.includes(targetDoc));
+ if (role) {
+ this.showRoleDocuments(role, group);
+ }
+ }
+
+ showAlert('Document signed successfully!');
+
+ } catch (error) {
+ console.error('Error signing document:', error);
+ showAlert(error instanceof Error ? error.message : 'Error signing document');
+ }
+ }
+
+
+ private initializeEventListeners() {
+ document.addEventListener('DOMContentLoaded', (): void => {
+ const newRequestBtn = this.shadowRoot?.getElementById('newRequestBtn');
+ if (newRequestBtn) {
+ newRequestBtn.addEventListener('click', (): void => {
+ this.newRequest({
+ processId: 0,
+ processName: '',
+ roleId: 0,
+ roleName: '',
+ documentId: 0,
+ documentName: ''
+ });
+ });
+ }
+ });
+
+ // 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.messagesMock = messageStore.getMessages();
+ if (this.messagesMock.length === 0) {
+ messageStore.setMessages(initialMessagesMock);
+ this.messagesMock = messageStore.getMessages();
+ }
+ this.updateCurrentUserDisplay();
+ this.initializeEventListeners();
+ this.loadGroupList();
+ }
+}
+
+customElements.define('signature-element', SignatureElement);
+export { SignatureElement };
+