clean_sign_translation

This commit is contained in:
Pascal 2024-11-27 12:07:36 +01:00
parent 57d5ccea09
commit 95d0186202
5 changed files with 221 additions and 217 deletions

View File

View File

@ -0,0 +1,7 @@
export interface Member {
id: string | number;
name: string;
email?: string;
avatar?: string;
processRoles?: Array<{ processId: number | string; role: string }>;
}

View File

@ -204,6 +204,46 @@ export const groupsMock = [
createdAt: null, createdAt: null,
deadline: null, deadline: null,
signatures: [] signatures: []
},
{
id: 11,
name: "Document Process D",
description: "Document vierge pour validation processus",
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.PENDING,
createdAt: "2024-01-15",
deadline: "2024-02-01",
signatures: [
{
member: { id: 3, name: "Charlie" },
signed: true,
signedAt: "2024-01-15"
},
{
member: { id: 4, name: "David" },
signed: false
}
]
},
{
id: 12,
name: "Document Process E",
description: "Document vierge pour validation processus",
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.PENDING,
createdAt: "2024-01-15",
deadline: "2024-02-01",
signatures: [
{
member: { id: 3, name: "Charlie" },
signed: true,
signedAt: "2024-01-15"
},
{
member: { id: 4, name: "David" },
signed: false
}
]
} }
] ]
}, },

View File

@ -8,7 +8,6 @@ declare global {
newRequest: typeof newRequest; newRequest: typeof newRequest;
submitRequest: typeof submitRequest; submitRequest: typeof submitRequest;
closeNewRequest: typeof closeNewRequest; closeNewRequest: typeof closeNewRequest;
removeMember: typeof removeMember;
closeModal: typeof closeModal; closeModal: typeof closeModal;
submitDocumentRequest: typeof submitDocumentRequest; submitDocumentRequest: typeof submitDocumentRequest;
submitNewDocument: typeof submitNewDocument; submitNewDocument: typeof submitNewDocument;
@ -30,45 +29,19 @@ import {
} from '../../models/signature.models'; } from '../../models/signature.models';
import { messageStore } from '../../utils/messageMock'; import { messageStore } from '../../utils/messageMock';
import { showAlert } from '../account/account'; import { showAlert } from '../account/account';
import { Member } from '../../interface/memberInterface';
import { Group } from '../../interface/groupInterface';
interface Member {
id: string | number;
name: string;
email?: string;
avatar?: string;
processRoles?: Array<{ processId: number | string; role: string }>;
}
let currentUser: Member = membersMock[0]; let currentUser: Member = membersMock[0];
interface Group {
id: number;
name: string;
description: string;
roles: Array<{
name: string;
members: Array<{ id: string | number; name: string }>;
documents?: Array<any>;
}>;
commonDocuments: Array<{
id: number;
name: string;
visibility: string;
description: string;
createdAt?: string | null;
deadline?: string | null;
signatures?: DocumentSignature[];
status?: string;
}>;
}
// Fonction pour gérer la liste des utilisateurs // Function to manage the list of users
function toggleUserList() { function toggleUserList() {
const userList = document.getElementById('userList'); const userList = document.getElementById('userList');
if (!userList) return; if (!userList) return;
if (!userList.classList.contains('show')) { if (!userList.classList.contains('show')) {
// Remplir la liste des utilisateurs
userList.innerHTML = membersMock.map(member => ` userList.innerHTML = membersMock.map(member => `
<div class="user-list-item" onclick="switchUser('${member.id}')"> <div class="user-list-item" onclick="switchUser('${member.id}')">
<span class="user-avatar">${member.avatar}</span> <span class="user-avatar">${member.avatar}</span>
@ -82,7 +55,7 @@ function toggleUserList() {
userList?.classList.toggle('show'); userList?.classList.toggle('show');
} }
// Fonction pour changer d'utilisateur // Function to switch user
function switchUser(userId: string | number) { function switchUser(userId: string | number) {
const user = membersMock.find(member => member.id === userId); const user = membersMock.find(member => member.id === userId);
if (!user) return; if (!user) return;
@ -92,7 +65,7 @@ function switchUser(userId: string | number) {
userList?.classList.remove('show'); userList?.classList.remove('show');
} }
// Fonction pour mettre à jour l'affichage de l'utilisateur // Function to update the display of the current user
function updateCurrentUserDisplay() { function updateCurrentUserDisplay() {
const userDisplay = document.getElementById('current-user'); const userDisplay = document.getElementById('current-user');
if (userDisplay) { if (userDisplay) {
@ -105,15 +78,15 @@ function updateCurrentUserDisplay() {
} }
} }
// Ajouter les fonctions au scope global // Add the functions to the global scope
window.toggleUserList = toggleUserList; window.toggleUserList = toggleUserList;
window.switchUser = switchUser; window.switchUser = switchUser;
// Initialiser l'affichage de l'utilisateur courant au chargement // Initialize the display of the current user when the page loads
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
updateCurrentUserDisplay(); updateCurrentUserDisplay();
// Fermer la liste si on clique ailleurs // Close the list if clicked elsewhere
document.addEventListener('click', (event) => { document.addEventListener('click', (event) => {
const userList = document.getElementById('userList'); const userList = document.getElementById('userList');
const userSwitchBtn = document.getElementById('userSwitchBtn'); const userSwitchBtn = document.getElementById('userSwitchBtn');
@ -122,7 +95,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}); });
// Initialiser les groupes en localStorage s'ils n'existent pas // Initialize the groups in localStorage if they don't exist
if (!localStorage.getItem('groups')) { if (!localStorage.getItem('groups')) {
localStorage.setItem('groups', JSON.stringify(groupsMock)); localStorage.setItem('groups', JSON.stringify(groupsMock));
} }
@ -146,11 +119,11 @@ function loadGroupList() {
const li = document.createElement('li'); const li = document.createElement('li');
li.className = 'group-list-item'; li.className = 'group-list-item';
// Créer un conteneur flex pour le nom et l'icône // Create a flex container for the name and the icon
const container = document.createElement('div'); const container = document.createElement('div');
container.className = 'group-item-container'; container.className = 'group-item-container';
// Span pour le nom du processus // Span for the process name
const nameSpan = document.createElement('span'); const nameSpan = document.createElement('span');
nameSpan.textContent = group.name; nameSpan.textContent = group.name;
nameSpan.className = 'process-name'; nameSpan.className = 'process-name';
@ -159,13 +132,12 @@ function loadGroupList() {
toggleRoles(group, li); toggleRoles(group, li);
}; };
// Ajouter l'icône ⚙️ avec un ID unique // Add the ⚙️ icon with a unique ID
const settingsIcon = document.createElement('span'); const settingsIcon = document.createElement('span');
settingsIcon.textContent = '⚙️'; settingsIcon.textContent = '⚙️';
settingsIcon.className = 'settings-icon'; settingsIcon.className = 'settings-icon';
settingsIcon.id = `settings-${group.id}`; // ID unique basé sur l'ID du groupe settingsIcon.id = `settings-${group.id}`; // Unique ID based on the group ID
// Créer une div pour la vue détaillée avec un ID unique correspondant
const detailsArea = document.createElement('div'); const detailsArea = document.createElement('div');
detailsArea.id = `process-details-${group.id}`; detailsArea.id = `process-details-${group.id}`;
detailsArea.className = 'process-details'; detailsArea.className = 'process-details';
@ -176,7 +148,7 @@ function loadGroupList() {
showProcessDetails(group, group.id); showProcessDetails(group, group.id);
}; };
// Assembler les éléments // Assemble the elements
container.appendChild(nameSpan); container.appendChild(nameSpan);
container.appendChild(settingsIcon); container.appendChild(settingsIcon);
li.appendChild(container); li.appendChild(container);
@ -198,18 +170,18 @@ function toggleRoles(group: Group, groupElement: HTMLElement) {
group.roles.forEach(role => { group.roles.forEach(role => {
const roleItem = document.createElement('li'); const roleItem = document.createElement('li');
// Créer un conteneur flex pour le nom et l'icône // Create a flex container for the name and the icon
const container = document.createElement('div'); const container = document.createElement('div');
container.className = 'role-item-container'; container.className = 'role-item-container';
container.style.display = 'flex'; container.style.display = 'flex';
container.style.justifyContent = 'space-between'; container.style.justifyContent = 'space-between';
container.style.alignItems = 'center'; container.style.alignItems = 'center';
// Span pour le nom du rôle // Span for the role name
const nameSpan = document.createElement('span'); const nameSpan = document.createElement('span');
nameSpan.textContent = role.name; nameSpan.textContent = role.name;
// Bouton dossier // Folder button
const folderButton = document.createElement('span'); const folderButton = document.createElement('span');
folderButton.textContent = '📁'; folderButton.textContent = '📁';
folderButton.className = 'folder-icon'; folderButton.className = 'folder-icon';
@ -218,7 +190,7 @@ function toggleRoles(group: Group, groupElement: HTMLElement) {
showRoleDocuments(role, group); showRoleDocuments(role, group);
}; };
// Assembler les éléments // Assemble the elements
container.appendChild(nameSpan); container.appendChild(nameSpan);
container.appendChild(folderButton); container.appendChild(folderButton);
roleItem.appendChild(container); roleItem.appendChild(container);
@ -234,7 +206,6 @@ function toggleRoles(group: Group, groupElement: HTMLElement) {
groupElement.appendChild(roleList); groupElement.appendChild(roleList);
} }
// Toggle the list of membres
function toggleMembers(role: { members: { id: string | number; name: string; }[] }, roleElement: HTMLElement) { function toggleMembers(role: { members: { id: string | number; name: string; }[] }, roleElement: HTMLElement) {
let memberList = roleElement.querySelector('.member-list'); let memberList = roleElement.querySelector('.member-list');
if (memberList) { if (memberList) {
@ -261,12 +232,12 @@ function toggleMembers(role: { members: { id: string | number; name: string; }[]
} }
// Load the list of members // Load the list of members
function loadMemberChat(memberId: string | number) { function loadMemberChat(memberId: string | number) {
selectedMemberId = String(memberId); selectedMemberId = String(memberId);
const memberMessages = messagesMock.find(m => String(m.memberId) === String(memberId)); const memberMessages = messagesMock.find(m => String(m.memberId) === String(memberId));
// Trouver le processus et le rôle du membre // Find the process and the role of the member
let memberInfo = { processName: '', roleName: '', memberName: '' }; let memberInfo = { processName: '', roleName: '', memberName: '' };
groupsMock.forEach(process => { groupsMock.forEach(process => {
process.roles.forEach(role => { process.roles.forEach(role => {
@ -349,15 +320,15 @@ function sendMessage() {
type: 'text' as const type: 'text' as const
}; };
// Ajouter et afficher le message immédiatement // Add and display the message immediately
messageStore.addMessage(selectedMemberId, newMessage); messageStore.addMessage(selectedMemberId, newMessage);
messagesMock = messageStore.getMessages(); messagesMock = messageStore.getMessages();
loadMemberChat(selectedMemberId); loadMemberChat(selectedMemberId);
// Réinitialiser l'input // Reset the input
messageInput.value = ''; messageInput.value = '';
// Réponse automatique après 2 secondes // Automatic response after 2 seconds
setTimeout(() => { setTimeout(() => {
if (selectedMemberId) { if (selectedMemberId) {
const autoReply = generateAutoReply(`Member ${selectedMemberId}`); const autoReply = generateAutoReply(`Member ${selectedMemberId}`);
@ -509,11 +480,11 @@ document.addEventListener('click', (event) => {
}); });
// ------------------ PROCESS DETAILS ------------------ // ------------------ PROCESS DETAILS ------------------
// Fonction pour afficher les détails du processus // Function to display the process details
function showProcessDetails(group: Group, groupId: number) { function showProcessDetails(group: Group, groupId: number) {
console.log('Showing details for group:', groupId); console.log('Showing details for group:', groupId);
// Fermer toutes les vues de processus existantes // Close all existing process views
const allDetailsAreas = document.querySelectorAll('.process-details'); const allDetailsAreas = document.querySelectorAll('.process-details');
allDetailsAreas.forEach(area => { allDetailsAreas.forEach(area => {
(area as HTMLElement).style.display = 'none'; (area as HTMLElement).style.display = 'none';
@ -525,11 +496,11 @@ function showProcessDetails(group: Group, groupId: number) {
return; return;
} }
// Charger les données du localStorage // Load the data from localStorage
const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]'); const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]');
const storedGroup = storedGroups.find((g: Group) => g.id === groupId); const storedGroup = storedGroups.find((g: Group) => g.id === groupId);
// Utiliser les données du localStorage si disponibles, sinon utiliser le groupe passé en paramètre // Use the data from localStorage if available, otherwise use the group passed as a parameter
const displayGroup = storedGroup || group; const displayGroup = storedGroup || group;
let detailsArea = document.getElementById(`process-details-${groupId}`); let detailsArea = document.getElementById(`process-details-${groupId}`);
@ -565,7 +536,6 @@ function showProcessDetails(group: Group, groupId: number) {
sig.member && 'id' in sig.member && sig.member.id === currentUser.id && !sig.signed sig.member && 'id' in sig.member && sig.member.id === currentUser.id && !sig.signed
); );
// Ajouter le bouton de signature pour tous les documents non vierges en mode dev
const signButton = !isVierge ? ` const signButton = !isVierge ? `
${totalSignatures > 0 && signedCount < totalSignatures && canSign ? ` ${totalSignatures > 0 && signedCount < totalSignatures && canSign ? `
<button class="sign-button" onclick="signDocument(${document.id}, ${groupId}, true)"> <button class="sign-button" onclick="signDocument(${document.id}, ${groupId}, true)">
@ -625,7 +595,7 @@ function showProcessDetails(group: Group, groupId: number) {
<div class="details-section"> <div class="details-section">
<h3>Roles and Documents</h3> <h3>Roles and Documents</h3>
${displayGroup.roles.map((role: { name: string; documents?: any[] }) => { ${displayGroup.roles.map((role: { name: string; documents?: any[] }) => {
// Filtrer les documents selon les droits d'accès // Filter the documents according to the access rights
const accessibleDocuments = (role.documents || []).filter(doc => const accessibleDocuments = (role.documents || []).filter(doc =>
canUserAccessDocument(doc, role.name, currentUser.processRoles?.[0]?.role || '') canUserAccessDocument(doc, role.name, currentUser.processRoles?.[0]?.role || '')
); );
@ -711,10 +681,6 @@ function showProcessDetails(group: Group, groupId: number) {
</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'); const newCloseProcessButton = document.createElement('button');
newCloseProcessButton.className = 'close-btn'; newCloseProcessButton.className = 'close-btn';
@ -723,13 +689,12 @@ function showProcessDetails(group: Group, groupId: number) {
const headerButtons = detailsArea.querySelector('.header-buttons'); const headerButtons = detailsArea.querySelector('.header-buttons');
if (headerButtons) { if (headerButtons) {
/**headerButtons.appendChild(newRequestBtn);**/
headerButtons.appendChild(newCloseProcessButton); headerButtons.appendChild(newCloseProcessButton);
} }
} }
} }
// Fonction pour fermer les détails et revenir au chat // Function to close the details and return to the chat
function closeProcessDetails(groupId: number) { function closeProcessDetails(groupId: number) {
const detailsArea = document.getElementById(`process-details-${groupId}`); const detailsArea = document.getElementById(`process-details-${groupId}`);
const chatArea = document.getElementById('chat-area'); const chatArea = document.getElementById('chat-area');
@ -746,7 +711,7 @@ function closeProcessDetails(groupId: number) {
window.closeProcessDetails = closeProcessDetails; window.closeProcessDetails = closeProcessDetails;
window.loadMemberChat = loadMemberChat; window.loadMemberChat = loadMemberChat;
// Nouvelle fonction pour afficher les documents d'un rôle // New function to display the documents of a role
function showRoleDocuments(role: { function showRoleDocuments(role: {
name: string; name: string;
documents?: Array<{ documents?: Array<{
@ -761,16 +726,16 @@ function showRoleDocuments(role: {
}>; }>;
id?: number; id?: number;
}, group: Group) { }, group: Group) {
// Charger les données depuis le localStorage // Load the data from localStorage
const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]'); const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]');
const storedGroup = storedGroups.find((g: Group) => g.id === group.id); const storedGroup = storedGroups.find((g: Group) => g.id === group.id);
const storedRole = storedGroup?.roles.find((r: any) => r.name === role.name); const storedRole = storedGroup?.roles.find((r: any) => r.name === role.name);
// Utiliser les données du localStorage si disponibles, sinon utiliser les données passées en paramètre // Use the data from localStorage if available, otherwise use the data passed as a parameter
const displayRole = storedRole || role; const displayRole = storedRole || role;
console.log('Showing documents for role:', displayRole.name, 'in group:', group.name); console.log('Showing documents for role:', displayRole.name, 'in group:', group.name);
// Fermer d'abord toutes les vues de documents existantes // Close all existing document views first
const allDetailsAreas = document.querySelectorAll('.process-details'); const allDetailsAreas = document.querySelectorAll('.process-details');
allDetailsAreas.forEach(area => { allDetailsAreas.forEach(area => {
area.remove(); area.remove();
@ -782,11 +747,11 @@ function showRoleDocuments(role: {
return; return;
} }
// Créer une nouvelle zone de détails // Create a new details area
const detailsArea = document.createElement('div'); const detailsArea = document.createElement('div');
detailsArea.id = `role-documents-${displayRole.name}`; detailsArea.id = `role-documents-${displayRole.name}`;
detailsArea.className = 'process-details'; detailsArea.className = 'process-details';
// Filtrer les documents accessibles // Filter the accessible documents
const accessibleDocuments = (displayRole.documents || []).filter((doc: { const accessibleDocuments = (displayRole.documents || []).filter((doc: {
name: string; name: string;
visibility: string; visibility: string;
@ -862,7 +827,7 @@ function showRoleDocuments(role: {
<p>${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)</p> <p>${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)</p>
</div> </div>
` : ` ` : `
<p>Document vierge - En attente de création</p> <p>Blank document - Waiting for creation</p>
${canUserAccessDocument(document, displayRole.name, currentUser.processRoles?.[0]?.role || '') ? ` ${canUserAccessDocument(document, displayRole.name, currentUser.processRoles?.[0]?.role || '') ? `
<button class="new-request-btn" onclick="newRequest({ <button class="new-request-btn" onclick="newRequest({
processId: ${group.id}, processId: ${group.id},
@ -887,7 +852,7 @@ function showRoleDocuments(role: {
container.appendChild(detailsArea); container.appendChild(detailsArea);
} }
// Fonction pour fermer la vue des documents d'un rôle // Function to close the documents view of a role
function closeRoleDocuments(roleName: string) { function closeRoleDocuments(roleName: string) {
const detailsArea = document.getElementById(`role-documents-${roleName}`); const detailsArea = document.getElementById(`role-documents-${roleName}`);
if (detailsArea) { if (detailsArea) {
@ -901,7 +866,7 @@ window.closeRoleDocuments = closeRoleDocuments;
window.switchUser = switchUser; window.switchUser = switchUser;
// Fonction pour calculer la durée entre deux dates // Function to calculate the duration between two dates
function calculateDuration(startDate: string | null | undefined, endDate: string | null | undefined): number { function calculateDuration(startDate: string | null | undefined, endDate: string | null | undefined): number {
const start = new Date(startDate || ''); const start = new Date(startDate || '');
const end = new Date(endDate || ''); const end = new Date(endDate || '');
@ -914,38 +879,27 @@ window.newRequest = newRequest;
window.submitRequest = submitRequest; window.submitRequest = submitRequest;
window.closeNewRequest = closeNewRequest; window.closeNewRequest = closeNewRequest;
// Définir la fonction removeMember pour supprimer un membre par son ID // Function to manage the new request
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) { function newRequest(params: RequestParams) {
// Ajout de validation des paramètres // Add parameter validation
if (!params || !params.processId) { if (!params || !params.processId) {
console.error('Paramètres invalides:', params); console.error('Paramètres invalides:', params);
showAlert('Paramètres invalides pour la nouvelle demande'); showAlert('Invalid parameters for new request');
return; return;
} }
const modal = document.createElement('div'); const modal = document.createElement('div');
modal.className = 'modal-overlay'; modal.className = 'modal-overlay';
// Récupérer le processus avec une vérification // Retrieve the process with a verification
const process = groupsMock.find(g => g.id === params.processId); const process = groupsMock.find(g => g.id === params.processId);
if (!process) { if (!process) {
console.error('Processus non trouvé:', params.processId); console.error('Processus non trouvé:', params.processId);
showAlert('Processus non trouvé'); showAlert('Process not found');
return; return;
} }
// Déterminer les membres avec une vérification supplémentaire // Determine the members with an additional verification
let membersToDisplay = []; let membersToDisplay = [];
try { try {
if (params.roleName === 'common') { if (params.roleName === 'common') {
@ -958,7 +912,7 @@ function newRequest(params: RequestParams) {
} else { } else {
const role = process.roles.find(r => r.name === params.roleName); const role = process.roles.find(r => r.name === params.roleName);
if (!role) { if (!role) {
throw new Error(`Rôle ${params.roleName} non trouvé`); throw new Error(`Role ${params.roleName} not found`);
} }
membersToDisplay = role.members.map(member => ({ membersToDisplay = role.members.map(member => ({
...member, ...member,
@ -966,8 +920,8 @@ function newRequest(params: RequestParams) {
})); }));
} }
} catch (error) { } catch (error) {
console.error('Erreur lors de la récupération des membres:', error); console.error('Error retrieving members:', error);
showAlert('Erreur lors de la récupération des membres'); showAlert('Error retrieving members');
return; return;
} }
@ -1067,12 +1021,12 @@ function newRequest(params: RequestParams) {
const fileInput = modal.querySelector('#fileInput') as HTMLInputElement; const fileInput = modal.querySelector('#fileInput') as HTMLInputElement;
const fileList = modal.querySelector('#fileList') as HTMLDivElement; const fileList = modal.querySelector('#fileList') as HTMLDivElement;
// Rendre la zone cliquable // Make the area clickable
dropZone.addEventListener('click', () => { dropZone.addEventListener('click', () => {
fileInput.click(); fileInput.click();
}); });
// Gérer la sélection de fichiers // Manage the file selection
fileInput.addEventListener('change', (e: Event) => { fileInput.addEventListener('change', (e: Event) => {
const target = e.target as HTMLInputElement; const target = e.target as HTMLInputElement;
if (target.files && target.files.length > 0) { if (target.files && target.files.length > 0) {
@ -1080,7 +1034,7 @@ function newRequest(params: RequestParams) {
} }
}); });
// Gérer le drag & drop // Manage the drag & drop
dropZone.addEventListener('dragover', (e: DragEvent) => { dropZone.addEventListener('dragover', (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
dropZone.classList.add('dragover'); dropZone.classList.add('dragover');
@ -1103,7 +1057,7 @@ function newRequest(params: RequestParams) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (e) => { reader.onload = (e) => {
const fileContent = e.target?.result; const fileContent = e.target?.result;
// Vérifier si le fichier n'est pas déjà dans la liste // Verify if the file is not already in the list
const existingFiles = fileList.querySelectorAll('.file-name'); const existingFiles = fileList.querySelectorAll('.file-name');
const isDuplicate = Array.from(existingFiles).some( const isDuplicate = Array.from(existingFiles).some(
existingFile => existingFile.textContent === file.name existingFile => existingFile.textContent === file.name
@ -1150,11 +1104,11 @@ function submitNewDocument(event: Event) {
const form = document.getElementById('newDocumentForm') as HTMLFormElement; const form = document.getElementById('newDocumentForm') as HTMLFormElement;
if (!form) { if (!form) {
showAlert('Formulaire non trouvé'); showAlert('Form not found');
return; return;
} }
// Récupération des fichiers // Retrieve the files
const fileList = document.getElementById('fileList'); const fileList = document.getElementById('fileList');
const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map(fileItem => { const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map(fileItem => {
const fileName = fileItem.querySelector('.file-name')?.textContent || ''; const fileName = fileItem.querySelector('.file-name')?.textContent || '';
@ -1164,7 +1118,7 @@ function submitNewDocument(event: Event) {
}; };
}); });
// Récupération des valeurs du formulaire // Retrieve the values from the form
const processId = Number((form.querySelector('#processId') as HTMLInputElement)?.value); const processId = Number((form.querySelector('#processId') as HTMLInputElement)?.value);
const documentId = Number((form.querySelector('#documentId') as HTMLInputElement)?.value); const documentId = Number((form.querySelector('#documentId') as HTMLInputElement)?.value);
const documentName = (form.querySelector('#documentName') as HTMLInputElement)?.value?.trim(); const documentName = (form.querySelector('#documentName') as HTMLInputElement)?.value?.trim();
@ -1174,17 +1128,17 @@ function submitNewDocument(event: Event) {
// Validation // Validation
if (!documentName || !description || !deadline) { if (!documentName || !description || !deadline) {
showAlert('Veuillez remplir tous les champs obligatoires'); showAlert('Please fill in all required fields');
return; return;
} }
try { try {
// Récupérer les données actuelles // Retrieve the current data
const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock)); const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock));
const group = groups.find((g: Group) => g.id === processId); const group = groups.find((g: Group) => g.id === processId);
if (!group) { if (!group) {
showAlert('Processus non trouvé'); showAlert('Process not found');
return; return;
} }
@ -1193,11 +1147,11 @@ function submitNewDocument(event: Event) {
}); });
if (!role) { if (!role) {
showAlert('Rôle non trouvé'); showAlert('Role not found');
return; return;
} }
// Créer le nouveau document avec les signatures des membres du rôle // Create the new document with the signatures of the role members
const updatedDocument = { const updatedDocument = {
id: documentId, id: documentId,
name: documentName, name: documentName,
@ -1214,16 +1168,16 @@ function submitNewDocument(event: Event) {
files: files // Ajout des fichiers au document files: files // Ajout des fichiers au document
}; };
// Mettre à jour le document dans le rôle // Update the document in the role
const documentIndex = role.documents.findIndex((d: any) => d.id === documentId); const documentIndex = role.documents.findIndex((d: any) => d.id === documentId);
if (documentIndex !== -1) { if (documentIndex !== -1) {
role.documents[documentIndex] = updatedDocument; role.documents[documentIndex] = updatedDocument;
} }
// Sauvegarder dans le localStorage // Save in localStorage
localStorage.setItem('groups', JSON.stringify(groups)); localStorage.setItem('groups', JSON.stringify(groups));
// Mettre à jour également groupsMock pour la cohérence // Also update groupsMock for consistency
const mockGroup = groupsMock.find(g => g.id === processId); const mockGroup = groupsMock.find(g => g.id === processId);
if (mockGroup) { if (mockGroup) {
const mockRole = mockGroup.roles.find(r => r.name === role.name); const mockRole = mockGroup.roles.find(r => r.name === role.name);
@ -1238,18 +1192,18 @@ function submitNewDocument(event: Event) {
} }
} }
// Fermer le modal // Close the modal
if (event.target instanceof HTMLElement) { if (event.target instanceof HTMLElement) {
closeModal(event.target); closeModal(event.target);
} }
// Recharger la vue des documents avec les données mises à jour // Reload the documents view with the updated data
showRoleDocuments(role, group); showRoleDocuments(role, group);
showAlert('Document mis à jour avec succès!'); showAlert('Document updated successfully!');
} catch (error) { } catch (error) {
console.error('Erreur lors de la sauvegarde:', error); console.error('Error saving:', error);
showAlert('Une erreur est survenue lors de la sauvegarde'); showAlert('An error occurred while saving');
} }
} }
@ -1260,7 +1214,7 @@ function submitCommonDocument(event: Event) {
const form = document.getElementById('newDocumentForm') as HTMLFormElement; const form = document.getElementById('newDocumentForm') as HTMLFormElement;
if (!form) { if (!form) {
showAlert('Formulaire non trouvé'); showAlert('Form not found');
return; return;
} }
@ -1272,7 +1226,7 @@ function submitCommonDocument(event: Event) {
const visibility = (form.querySelector('#visibility') as HTMLSelectElement)?.value; const visibility = (form.querySelector('#visibility') as HTMLSelectElement)?.value;
if (!documentName || !description || !deadline) { if (!documentName || !description || !deadline) {
showAlert('Veuillez remplir tous les champs obligatoires'); showAlert('Please fill in all required fields');
return; return;
} }
@ -1281,11 +1235,11 @@ function submitCommonDocument(event: Event) {
const group = groups.find((g: Group) => g.id === processId); const group = groups.find((g: Group) => g.id === processId);
if (!group) { if (!group) {
showAlert('Processus non trouvé'); showAlert('Process not found');
return; return;
} }
// Récupérer tous les membres de tous les rôles du groupe // Retrieve all members of all roles in the group
const allMembers = group.roles.reduce((acc: any[], role: any) => { const allMembers = group.roles.reduce((acc: any[], role: any) => {
return acc.concat(role.members); return acc.concat(role.members);
}, []); }, []);
@ -1315,7 +1269,7 @@ function submitCommonDocument(event: Event) {
files: files files: files
}; };
// Mettre à jour le document commun // Update the common document
const documentIndex = group.commonDocuments.findIndex((d: { id: number }) => d.id === documentId); const documentIndex = group.commonDocuments.findIndex((d: { id: number }) => d.id === documentId);
if (documentIndex !== -1) { if (documentIndex !== -1) {
group.commonDocuments[documentIndex] = updatedDocument; group.commonDocuments[documentIndex] = updatedDocument;
@ -1328,11 +1282,11 @@ function submitCommonDocument(event: Event) {
} }
showProcessDetails(group, group.id); showProcessDetails(group, group.id);
showAlert('Document commun mis à jour avec succès!'); showAlert('Document common updated successfully!');
} catch (error) { } catch (error) {
console.error('Erreur lors de la sauvegarde:', error); console.error('Error saving:', error);
showAlert('Une erreur est survenue lors de la sauvegarde'); showAlert('An error occurred while saving');
} }
} }
@ -1461,64 +1415,9 @@ if (addMembersBtn) {
}); });
} }
// Fonction d'initialisation pour le router // FUNCTIONS FOR SIGNATURE
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 // Add this function to sign a document
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();
}
});
}
}
// Ajouter cette interface pour la signature
interface SignatureResponse {
success: boolean;
message: string;
documentId: number;
memberId: string | number;
signedAt: string;
}
// Ajouter cette fonction pour signer un document
function signDocument(documentId: number, processId: number, isCommonDocument: boolean = false) { function signDocument(documentId: number, processId: number, isCommonDocument: boolean = false) {
if (typeof window === 'undefined' || typeof document === 'undefined') { if (typeof window === 'undefined' || typeof document === 'undefined') {
console.error('Cette fonction ne peut être exécutée que dans un navigateur'); console.error('Cette fonction ne peut être exécutée que dans un navigateur');
@ -1530,7 +1429,7 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
const group = groups.find((g: Group) => g.id === processId); const group = groups.find((g: Group) => g.id === processId);
if (!group) { if (!group) {
throw new Error('Processus non trouvé'); throw new Error('Process not found');
} }
let targetDoc; let targetDoc;
@ -1546,7 +1445,7 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
} }
if (!targetDoc) { if (!targetDoc) {
throw new Error('Document non trouvé'); throw new Error('Document not found');
} }
const canSign = isCommonDocument ? const canSign = isCommonDocument ?
@ -1556,11 +1455,11 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
canUserSignDocument(targetDoc, currentUser?.name, currentUser); canUserSignDocument(targetDoc, currentUser?.name, currentUser);
if (!canSign) { if (!canSign) {
showAlert("Vous n'avez pas les droits nécessaires pour signer ce document."); showAlert("You do not have the necessary rights to sign this document.");
return; return;
} }
// Créer et insérer la modal directement dans le body // Create and insert the modal directly into the body
const modalHtml = ` const modalHtml = `
<div class="modal-overlay" id="signatureModal"> <div class="modal-overlay" id="signatureModal">
<div class="modal-document"> <div class="modal-document">
@ -1590,7 +1489,7 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
<div class="description-section"> <div class="description-section">
<h4>Description:</h4> <h4>Description:</h4>
<p>${targetDoc.description || 'Aucune description disponible'}</p> <p>${targetDoc.description || 'No description available'}</p>
</div> </div>
<div class="signatures-section"> <div class="signatures-section">
@ -1601,8 +1500,8 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
<span class="signer-name">${sig.member.name}</span> <span class="signer-name">${sig.member.name}</span>
<span class="signature-status"> <span class="signature-status">
${sig.signed ? ${sig.signed ?
`✓ Signé le ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'date inconnue'}` : `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'unknown date'}` :
'⌛ En attente'} '⌛ Pending'}
</span> </span>
</div> </div>
`).join('')} `).join('')}
@ -1623,20 +1522,20 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
</a> </a>
</div> </div>
`).join('') `).join('')
: '<p>Aucun fichier joint</p>' : '<p>No file attached</p>'
} }
</div> </div>
</div> </div>
` : ''} ` : ''}
<div class="confirmation-section"> <div class="confirmation-section">
<p class="warning-text">En signant ce document, vous confirmez avoir pris connaissance de son contenu.</p> <p class="warning-text">By signing this document, you confirm that you have read its contents.</p>
<div class="signature-slider-container"> <div class="signature-slider-container">
<div class="slider-track"> <div class="slider-track">
<div class="slider-handle" id="signatureSlider" draggable="true"> <div class="slider-handle" id="signatureSlider" draggable="true">
<span class="slider-arrow"></span> <span class="slider-arrow"></span>
</div> </div>
<span class="slider-text">Glisser pour signer</span> <span class="slider-text">Drag to sign</span>
</div> </div>
</div> </div>
</div> </div>
@ -1647,7 +1546,7 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
document.body.insertAdjacentHTML('beforeend', modalHtml); document.body.insertAdjacentHTML('beforeend', modalHtml);
// Ajouter la logique du slider après création de la modal // Add the slider logic after creating the modal
const slider = document.getElementById('signatureSlider'); const slider = document.getElementById('signatureSlider');
const sliderTrack = slider?.parentElement; const sliderTrack = slider?.parentElement;
let isDragging = false; let isDragging = false;
@ -1659,7 +1558,7 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
document.addEventListener('mousemove', drag); document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDrag); document.addEventListener('mouseup', stopDrag);
// Touch events pour mobile // Touch events for mobile
slider.addEventListener('touchstart', initDrag); slider.addEventListener('touchstart', initDrag);
document.addEventListener('touchmove', drag); document.addEventListener('touchmove', drag);
document.addEventListener('touchend', stopDrag); document.addEventListener('touchend', stopDrag);
@ -1678,17 +1577,17 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
const rect = sliderTrack.getBoundingClientRect(); const rect = sliderTrack.getBoundingClientRect();
const x = 'touches' in e ? e.touches[0].clientX : e.clientX; const x = 'touches' in e ? e.touches[0].clientX : e.clientX;
// Calculer la position relative à la track // Calculate the position relative to the track
let newLeft = x - rect.left - (slider.offsetWidth / 2); let newLeft = x - rect.left - (slider.offsetWidth / 2);
// Limiter le déplacement // Limit the movement
const maxLeft = sliderTrack.offsetWidth - slider.offsetWidth; const maxLeft = sliderTrack.offsetWidth - slider.offsetWidth;
newLeft = Math.max(0, Math.min(newLeft, maxLeft)); newLeft = Math.max(0, Math.min(newLeft, maxLeft));
// Mettre à jour la position // Update the position
slider.style.left = `${newLeft}px`; slider.style.left = `${newLeft}px`;
// Si le slider atteint 90% du chemin, déclencher la signature // If the slider reaches 90% of the path, trigger the signature
if (newLeft > maxLeft * 0.9) { if (newLeft > maxLeft * 0.9) {
stopDrag(e); stopDrag(e);
confirmSignature(documentId, processId, isCommonDocument); confirmSignature(documentId, processId, isCommonDocument);
@ -1699,29 +1598,29 @@ function signDocument(documentId: number, processId: number, isCommonDocument: b
if (!isDragging || !slider) return; if (!isDragging || !slider) return;
isDragging = false; isDragging = false;
// Réinitialiser la position si pas assez glissé // Reset the position if not enough dragged
if (slider.offsetLeft < (sliderTrack?.offsetWidth || 0) * 0.9) { if (slider.offsetLeft < (sliderTrack?.offsetWidth || 0) * 0.9) {
slider.style.left = '0px'; slider.style.left = '0px';
} }
} }
} catch (error) { } catch (error) {
console.error('Erreur lors de l\'affichage de la modal:', error); console.error('Error displaying modal:', error);
showAlert(error instanceof Error ? error.message : 'Erreur lors de l\'affichage de la modal'); showAlert(error instanceof Error ? error.message : 'Error displaying modal');
} }
} }
// Nouvelle fonction pour confirmer la signature // New function to confirm the signature
function confirmSignature(documentId: number, processId: number, isCommonDocument: boolean) { function confirmSignature(documentId: number, processId: number, isCommonDocument: boolean) {
try { try {
// Ajout du console.log pour voir l'utilisateur actuel // Add console.log to see the current user
console.log('Current user:', currentUser); console.log('Current user:', currentUser);
const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock)); const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock));
const group = groups.find((g: Group) => g.id === processId); const group = groups.find((g: Group) => g.id === processId);
if (!group) { if (!group) {
throw new Error('Processus non trouvé'); throw new Error('Process not found');
} }
let targetDoc; let targetDoc;
@ -1737,7 +1636,7 @@ function confirmSignature(documentId: number, processId: number, isCommonDocumen
} }
if (!targetDoc) { if (!targetDoc) {
throw new Error('Document non trouvé'); throw new Error('Document not found');
} }
const userSignature = targetDoc.signatures.find((sig: DocumentSignature) => const userSignature = targetDoc.signatures.find((sig: DocumentSignature) =>
@ -1745,7 +1644,7 @@ function confirmSignature(documentId: number, processId: number, isCommonDocumen
); );
if (!userSignature) { if (!userSignature) {
throw new Error(`L'utilisateur ${currentUser.name} n'est pas autorisé à signer ce document. Veuillez vous connecter avec un utilisateur autorisé.`); 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.signed = true;
@ -1753,7 +1652,6 @@ function confirmSignature(documentId: number, processId: number, isCommonDocumen
localStorage.setItem('groups', JSON.stringify(groups)); localStorage.setItem('groups', JSON.stringify(groups));
// Modification ici : utiliser document.querySelector au lieu de document
const closeBtn = document.querySelector('.modal-overlay .close-btn'); const closeBtn = document.querySelector('.modal-overlay .close-btn');
if (closeBtn instanceof HTMLElement) { if (closeBtn instanceof HTMLElement) {
closeModal(closeBtn); closeModal(closeBtn);
@ -1768,11 +1666,11 @@ function confirmSignature(documentId: number, processId: number, isCommonDocumen
} }
} }
showAlert('Document signé avec succès!'); showAlert('Document signed successfully!');
} catch (error) { } catch (error) {
console.error('Erreur lors de la signature:', error); console.error('Error signing document:', error);
showAlert(error instanceof Error ? error.message : 'Erreur lors de la signature'); showAlert(error instanceof Error ? error.message : 'Error signing document');
} }
} }
@ -1784,24 +1682,83 @@ if (typeof window !== 'undefined') {
(window as any).confirmSignature = confirmSignature; (window as any).confirmSignature = confirmSignature;
} }
// Ajouter cette fonction helper // Add this helper function
function canUserAccessDocument(document: any, roleId: string, currentUserRole: string): boolean { function canUserAccessDocument(document: any, roleId: string, currentUserRole: string): boolean {
// Modification de la logique d'accès // Modify the access logic
if (document.visibility === 'public') { if (document.visibility === 'public') {
return true; // Peut voir mais pas forcément signer return true; // Can see but not necessarily sign
} }
return roleId === currentUserRole; return roleId === currentUserRole;
} }
function canUserSignDocument(document: any, role: string, user: Member): boolean { function canUserSignDocument(document: any, role: string, user: Member): boolean {
// Vérifier si l'utilisateur a le bon rôle pour ce processus console.log('Checking signing rights for:', {
const userRole = user.processRoles?.find(pr => pr.role === role); document,
if (!userRole) { role,
return false; user,
} userRoles: user.processRoles
});
// Vérifier si l'utilisateur est dans la liste des signataires
return document.signatures?.some((sig: DocumentSignature) => // 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 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;
} }
// Function to initialize the router
export function initSignature() {
// Reset the display
const groupList = document.getElementById('group-list');
if (groupList) {
groupList.innerHTML = ''; // Clear the existing list
}
// Reload the messages from the store
messagesMock = messageStore.getMessages();
if (messagesMock.length === 0) {
messageStore.setMessages(initialMessagesMock);
messagesMock = messageStore.getMessages();
}
// Reset the interface
updateCurrentUserDisplay();
loadGroupList();
// If a member was selected, reload their 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');
}
});
// Reattach the event listeners for the 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();
}
});
}
}