4NK_node/ihm_client/dist/signature-component-DK4oRzT9.mjs

2105 lines
143 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

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

import { g as getCorrectDOM } from './document.utils-SxcZpxzG.mjs';
import { S as Services } from './index-C1Gp83RD.mjs';
const signatureStyle = "/* Styles de base */\n:root {\n --primary-color: #3A506B;\n /* Bleu métallique */\n --secondary-color: #B0BEC5;\n /* Gris acier */\n --accent-color: #D68C45;\n /* Cuivre */\n}\n\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n padding: 0;\n display: flex;\n height: 100vh;\n background-color: #e9edf1;\n flex-direction: column;\n}\n\n\n\n/* 4NK NAVBAR */\n\n.brand-logo {\n text-align: center;\n font-size: 1.5em;\n font-weight: bold;\n}\n\n.nav-wrapper {\n position: fixed;\n background: radial-gradient(circle, white, var(--primary-color));\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #37474F;\n height: 9vh;\n width: 100vw;\n left: 0;\n top: 0;\n box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);\n}\n\n/* Icônes de la barre de navigation */\n.nav-right-icons {\n display: flex;\n}\n\n.notification-bell,\n.burger-menu {\n height: 20px;\n width: 20px;\n margin-right: 1rem;\n cursor: pointer;\n}\n\n.notification-container {\n position: relative;\n /* Conserve la position pour le notification-board */\n display: inline-flex;\n align-items: center;\n}\n\n.notification-board {\n position: absolute;\n /* Position absolue pour le placer par rapport au container */\n top: 40px;\n right: 0;\n background-color: white;\n border: 1px solid #ccc;\n padding: 10px;\n width: 200px;\n max-height: 300px;\n overflow-y: auto;\n /* Scroll si les notifications dépassent la taille */\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n z-index: 10;\n /* Définit la priorité d'affichage au-dessus des autres éléments */\n display: none;\n /* Par défaut, la notification est masquée */\n}\n\n.notification-item{\n cursor: pointer;\n}\n\n.notification-badge {\n position: absolute;\n top: -18px;\n right: 35px;\n background-color: red;\n color: white;\n border-radius: 50%;\n padding: 4px 8px;\n font-size: 12px;\n display: none;\n /* S'affiche seulement lorsqu'il y a des notifications */\n z-index: 10;\n}\n\n/* Par défaut, le menu est masqué */\n#menu {\n display: none;\n /* Menu caché par défaut */\n transition: display 0.3s ease-in-out;\n}\n\n.burger-menu {\n cursor: pointer;\n}\n\n/* Icône burger */\n#burger-icon {\n cursor: pointer;\n}\n\n.menu-content {\n display: none;\n position: absolute;\n top: 3.4rem;\n right: 1rem;\n background-color: white;\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n border-radius: 5px;\n overflow: hidden;\n}\n\n.menu-content a {\n display: block;\n padding: 10px 20px;\n text-decoration: none;\n color: #333;\n border-bottom: 1px solid #e0e0e0;\n\n &:hover {\n background-color: rgba(26, 28, 24, .08);\n }\n}\n\n.menu-content a:last-child {\n border-bottom: none;\n}\n\n/* Ajustement pour la barre de navigation fixe */\n.container {\n display: flex;\n flex: 1;\n height: 90vh;\n margin-top: 9vh;\n margin-left: -1%;\n text-align: left;\n width: 209vh;\n}\n\n\n/* Liste des groupes */\n\n.group-list {\n width: 25%;\n background-color: #1f2c3d;\n color: white;\n padding: 20px;\n box-sizing: border-box;\n overflow-y: auto;\n border-right: 2px solid #2c3e50;\n flex-shrink: 0;\n padding-right: 10px;\n height: 91vh;\n}\n\n.group-list ul {\n cursor: pointer;\n list-style: none;\n padding: 0;\n padding-right: 10px;\n margin-left: 20px;\n}\n\n.group-list li {\n margin-bottom: 10px;\n padding: 15px;\n border-radius: 8px;\n background-color: #273646;\n cursor: pointer;\n transition: background-color 0.3s, box-shadow 0.3s;\n}\n\n.group-list li:hover {\n background-color: #34495e;\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);\n}\n\n\n/* Zone de chat */\n\n.chat-area {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n background-color: #ffffff;\n border-radius: 10px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);\n margin: 0px;\n margin-top: 20px;\n margin-left: 1%;\n margin-bottom: -7px;\n}\n\n/* En-tête du chat */\n.chat-header {\n background-color: #34495e;\n color: white;\n padding: 15px;\n font-size: 20px;\n font-weight: bold;\n border-radius: 10px 10px 0 0;\n text-align: center;\n}\n\n/* Messages */\n.messages {\n flex: 1;\n padding: 20px;\n overflow-y: auto;\n background-color: #f1f1f1;\n border-top: 1px solid #ddd;\n}\n\n.message-container {\n max-width: 100%;\n border-radius: 5px;\n overflow-wrap: break-word;\n word-wrap: break-word;\n background-color: #f1f1f1;\n display: flex; \n flex-direction: column; \n}\n\n.message-container .message {\n align-self: flex-start; \n}\n\n.message-container .message.user {\n align-self: flex-end; \n margin-left: auto; \n color: white;\n}\n\n.message {\n padding: 12px 18px;\n background-color: #e1e1e1;\n border-radius: 15px;\n max-width: 70%;\n font-size: 16px;\n line-height: 1.4;\n margin-bottom: 0%;\n white-space: pre-wrap;\n word-wrap: break-word;\n position: relative;\n display: inline-block;\n}\n\n/* Messages de l'utilisateur */\n.message.user {\n background-color: #3498db;\n color: white;\n align-self: flex-end;\n text-align: right;\n}\n\n/* Amélioration de l'esthétique des messages */\n/* .message.user:before {\n content: '';\n position: absolute;\n top: 10px;\n right: -10px;\n border: 10px solid transparent;\n border-left-color: #3498db;\n} */\n\n/* Zone de saisie */\n.input-area {\n padding: 10px;\n background-color: #bdc3c7;\n display: flex;\n align-items: center;\n /* Alignement vertical */\n}\n\n.input-area input[type=\"text\"] {\n flex: 1;\n /* Prend l'espace restant */\n padding: 10px;\n border: 1px solid #ccc;\n border-radius: 5px;\n}\n\n.input-area .attachment-icon {\n margin: 0 10px;\n cursor: pointer;\n display: flex;\n align-items: center;\n}\n\n.input-area button {\n padding: 10px;\n margin-left: 10px;\n background-color: #2980b9;\n color: white;\n border: none;\n border-radius: 5px;\n cursor: pointer;\n}\n\n.input-area button:hover {\n background-color: #1f608d;\n}\n\n#message-input {\n width: 100%;\n height: 50px;\n resize: none;\n padding: 10px;\n box-sizing: border-box;\n overflow: auto;\n max-width: 100%;\n border-radius: 10px;\n}\n\n/* Responsive */\n@media screen and (max-width: 768px) {\n .group-list {\n display: none;\n /* Masquer la liste des groupes sur les petits écrans */\n }\n\n .chat-area {\n margin: 0;\n }\n}\n\n#process-details {\n flex: 1;\n background: white;\n border-radius: 8px;\n margin: 10px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n display: none;\n overflow: hidden;\n}\n\n.process-details-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px;\n background: #f8f9fa;\n border-bottom: 1px solid #eee;\n border-radius: 8px 8px 0 0;\n}\n\n.process-details-header h2 {\n margin: 0;\n color: #333;\n}\n\n.close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n}\n\n.close-btn:hover {\n color: #333;\n}\n\n.process-details-content {\n padding: 20px;\n overflow-y: auto;\n height: calc(100% - 70px);\n}\n\n.details-section {\n margin-bottom: 30px;\n}\n\n.details-section h3 {\n color: #444;\n margin-bottom: 15px;\n padding-bottom: 8px;\n border-bottom: 2px solid #f0f0f0;\n}\n\n.documents-list {\n list-style: none;\n padding: 0;\n}\n\n.documents-list li {\n padding: 8px 0;\n border-bottom: 1px solid #eee;\n}\n\n.roles-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 20px;\n}\n\n.role-block {\n background: #f8f9fa;\n border-radius: 8px;\n padding: 15px;\n border: 1px solid #eee;\n}\n\n.role-block h4 {\n color: #555;\n margin: 0 0 10px 0;\n padding-bottom: 8px;\n border-bottom: 1px solid #eee;\n}\n\n.members-list {\n list-style: none;\n padding: 0;\n}\n\n.members-list li {\n padding: 8px 12px;\n margin: 4px 0;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.2s;\n}\n\n.members-list li:hover {\n background-color: #e9ecef;\n}\n\n.group-list-item {\n padding: 8px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n\n.group-item-container {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n}\n\n.process-name {\n flex-grow: 1;\n cursor: pointer;\n}\n\n.settings-icon {\n cursor: pointer;\n padding: 5px 8px;\n margin-left: 10px;\n border-radius: 4px;\n}\n\n.settings-icon:hover {\n background-color: rgba(255, 255, 255, 0.1);\n}\n\n.process-details {\n position: fixed;\n top: 9vh;\n right: 0;\n bottom: 0;\n left: 23.5%;\n background-color: white;\n box-sizing: border-box;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n border-radius: 8px;\n margin: 10px;\n z-index: 990;\n}\n\n.process-details-content {\n height: calc(100% - 60px); /* Ajusté pour tenir compte du header */\n overflow-y: auto;\n padding: 20px;\n}\n\n.documents-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 20px;\n margin-top: 15px;\n}\n\n.document-card {\n background: white;\n border-radius: 8px;\n padding: 15px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n border: 1px solid #eee;\n}\n\n.document-card.public {\n border-left: 4px solid #4CAF50;\n}\n\n.document-card.private {\n border-left: 4px solid #FFC107;\n}\n\n.document-card.confidential {\n border-left: 4px solid #F44336;\n}\n\n.document-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 10px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n}\n\n.document-header h4 {\n margin: 0;\n color: #333;\n}\n\n.document-visibility {\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: bold;\n}\n\n.public .document-visibility {\n background-color: #E8F5E9;\n color: #2E7D32;\n}\n\n.private .document-visibility {\n background-color: #FFF3E0;\n color: #F57C00;\n}\n\n.confidential .document-visibility {\n background-color: #FFEBEE;\n color: #C62828;\n}\n\n.document-info {\n margin: 10px 0;\n font-size: 14px;\n color: #666;\n}\n\n.document-info p {\n margin: 5px 0;\n}\n\n.signatures-list {\n margin-top: 10px;\n}\n\n.signature-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px;\n border-radius: 4px;\n margin: 5px 0;\n background-color: #f8f9fa;\n}\n\n.signature-item.signed {\n background-color: #E8F5E9;\n}\n\n.signature-item.pending {\n background-color: #FFF3E0;\n}\n\n.signer-name {\n font-weight: 500;\n}\n\n.signature-status {\n font-size: 12px;\n}\n\n.signed .signature-status {\n color: #2E7D32;\n}\n\n.pending .signature-status {\n color: #F57C00;\n}\n\n.user-selector {\n position: relative;\n margin-right: 20px;\n}\n\n#userSwitchBtn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 8px 12px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n color: var(--primary-color);\n}\n\n#userSwitchBtn:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.current-user-info {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.user-avatar {\n background-color: var(--primary-color);\n color: white;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: bold;\n}\n\n.user-list {\n position: absolute;\n top: 100%;\n right: 0;\n background: white;\n border-radius: 4px;\n box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n display: none;\n z-index: 1000;\n max-height: 400px;\n overflow-y: auto;\n width: 250px;\n}\n\n.user-list.show {\n display: block;\n}\n\n.user-list-item {\n padding: 8px 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n}\n\n.user-list-item:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.user-list-item .user-avatar {\n width: 24px;\n height: 24px;\n font-size: 12px;\n}\n\n.user-list-item .user-email {\n font-size: 12px;\n color: #666;\n display: block;\n}\n\n.document-card {\n margin-bottom: 20px;\n padding: 10px;\n border: 1px solid #ccc;\n border-radius: 5px;\n}\n\n.progress-bar {\n background-color: #f3f3f3;\n border-radius: 5px;\n height: 10px;\n width: 100%;\n margin-top: 5px;\n}\n\n.progress {\n background-color: #4caf50; /* Couleur de la barre de progression */\n height: 100%;\n border-radius: 5px;\n}\n\n.new-request-btn {\n background-color: #4caf50; \n color: white; \n border: none;\n border-radius: 5px; \n padding: 10px 15px; \n cursor: pointer; \n margin-left: 10px;\n}\n\n.new-request-btn:hover {\n background-color: #45a049; \n}\n\n.header-buttons {\n display: flex;\n align-items: center; \n gap: 10px; \n}\n\n.close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n}\n\n.close-btn:hover {\n color: #333;\n}\n\n.new-request-view {\n padding: 20px;\n background-color: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n margin: 20px;\n}\n\n.upload-area {\n border: 2px dashed #ccc;\n padding: 20px;\n text-align: center;\n margin: 20px 0;\n}\n\n.upload-icon {\n font-size: 50px;\n margin: 10px 0;\n}\n\n/* New Request View */\n.new-request-view {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: white;\n z-index: 1000; \n padding: 20px;\n box-sizing: border-box;\n overflow-y: auto;\n}\n\n.upload-area {\n border: 2px dashed #ccc;\n padding: 40px;\n text-align: center;\n margin: 20px auto;\n max-width: 600px;\n background-color: #f9f9f9;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.upload-icon {\n font-size: 50px;\n margin: 20px 0;\n}\n\n.new-request-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: 60px;\n padding: 0 20px;\n}\n\n.upload-area {\n border: 2px dashed #ccc;\n padding: 40px;\n text-align: center;\n margin: 20px auto;\n max-width: 600px;\n background-color: #f9f9f9;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.details-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #f8f9fa;\n padding: 5px 20px;\n border-radius:20px;\n}\n\n.header-buttons {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-direction: row; \n}\n\n.close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n padding: 5px;\n}\n\n.close-btn:hover {\n color: #333;\n}\n\n.new-request-btn {\n background-color: #4caf50;\n color: white;\n border: none;\n border-radius: 5px;\n padding: 8px 16px;\n cursor: pointer;\n /* margin-left: 10px; <- Supprimez cette ligne si elle existe */\n}\n\n.new-request-btn:hover {\n background-color: #45a049;\n}\n\n/* Ajoutez ces styles à votre fichier CSS existant */\n.document-card.vierge {\n background-color: #fff3cd;\n border: 2px solid #ffeeba;\n}\n\n.document-card.vierge .document-header {\n background-color: #fff3cd;\n}\n\n.document-card.vierge h4 {\n color: #856404;\n}\n\n.document-card.vierge .document-info {\n color: #856404;\n}\n\n/* Style pour l'emoji d'avertissement */\n.document-card.vierge h4::before {\n margin-right: 8px;\n}\n\n.vierge-documents-container {\n padding: 20px;\n overflow-y: auto;\n max-height: calc(100vh - 150px);\n}\n\n.document-form {\n padding: 15px;\n background-color: #fff;\n border-radius: 0 0 5px 5px;\n display: flex;\n gap: 20px;\n}\n\n.form-left {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 15px;\n}\n\n.form-right {\n display: flex;\n flex-direction: column;\n align-items: flex-end; /* Aligner le bouton à droite */\n}\n\n.form-group {\n flex: 2;\n display: flex;\n flex-direction: column;\n margin-bottom: 15px;\n}\n\n.form-group-members {\n flex: 2;\n display: flex;\n flex-direction: column;\n margin-bottom: 15px;\n font-weight: bold;\n}\n\n.submit-btn {\n background-color: #4caf50; \n color: white; \n border: none;\n border-radius: 5px; \n padding: 10px 15px; \n cursor: pointer; \n margin-left: 47%;\n margin-top: 20px;\n}\n\n.submit-btn:hover {\n background-color: #45a049;\n}\n\n.upload-format {\n font-size: 12px;\n color: #666;\n margin: 5px 0;\n}\n\n.browse-btn {\n background-color: #4caf50;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 10px;\n}\n\n.browse-btn:hover {\n background-color: #45a049;\n}\n\n.document-selector {\n padding: 20px;\n margin-bottom: 20px;\n}\n\n.document-selector label {\n display: block;\n margin-bottom: 10px;\n font-weight: bold;\n color: #666;\n}\n\n.document-selector select {\n width: 100%;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 16px;\n background-color: white;\n}\n\n#selected-document-form {\n padding: 0 20px;\n overflow: auto;\n height: 65%;\n}\n\n/* Style pour l'option avec l'emoji */\n.document-selector select option {\n padding: 10px;\n font-size: 14px;\n}\n\n.members-selection {\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #ddd;\n border-radius: 4px;\n padding: 10px;\n background-color: white;\n}\n\n.member-checkbox {\n display: flex;\n align-items: center;\n margin-bottom: 8px;\n padding: 5px;\n}\n\n.member-checkbox:hover {\n background-color: #f5f5f5;\n}\n\n.member-checkbox input[type=\"checkbox\"] {\n margin-right: 10px;\n}\n\n.member-checkbox label {\n cursor: pointer;\n flex: 1;\n}\n\n/* Style pour le conteneur des membres */\n.members-selection-container {\n display: flex;\n flex-direction: column;\n align-items: center; /* Centrer la liste des membres */\n width: 100%;\n}\n\n#members-list {\n width: 60%;\n height: 100%;\n margin-bottom: -40px;\n margin-top: 10px;\n}\n\n/* Style pour le label des membres */\n.members-selection-container label {\n font-weight: bold; /* Mettre le texte en gras */\n margin-bottom: 10px; /* Espacement en bas */\n display: block; /* Affichage en bloc */\n}\n\n/* Style pour les cases à cocher des membres */\n.member-checkbox {\n display: flex;\n align-items: center;\n margin-bottom: 8px;\n padding: 5px;\n border-radius: 4px; /* Coins arrondis */\n transition: background-color 0.2s; /* Transition pour l'effet de survol */\n}\n\n.member-checkbox:hover {\n background-color: #e9ecef; /* Couleur de fond au survol */\n}\n\n/* Style pour les labels */\n.form-group label {\n font-weight: bold; \n margin-bottom: 5px; \n}\n\n/* Style pour les champs de saisie */\n.form-group input,\n.form-group select {\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 16px;\n width: 80%;\n}\n\n.add-members-btn {\n background-color: #4caf50; \n color: white; \n border: none; \n border-radius: 5px; \n padding: 10px 15px; \n cursor: pointer; \n margin-top: 10px; /* Espacement au-dessus du bouton */\n}\n\n.add-members-btn:hover {\n background-color: #45a049; \n}\n\n/* Styles pour l'overlay et la modale */\n.modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n z-index: 999;\n}\n\n.modal {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background-color: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n z-index: 1000;\n width: 25%;\n max-width: 500px;\n}\n\n.modal-content {\n max-height: 70vh;\n overflow-y: auto;\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n margin-top: 20px;\n padding-top: 10px;\n border-top: 1px solid #eee;\n}\n\n.confirm-btn {\n background-color: #4caf50;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.confirm-btn:hover {\n background-color: #45a049;\n}\n\n.selected-member {\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #f5f5f5;\n padding: 8px 12px;\n margin: 4px 0;\n border-radius: 4px;\n}\n\n.cancel-btn {\n background-color: #df2020; \n color: white; \n border: none;\n padding: 8px 16px; \n border-radius: 4px; \n cursor: pointer;\n}\n\n.cancel-btn:hover {\n background-color: #c62828; /\n}\n\n.remove-member-btn {\n background-color: #dc3545;\n color: white;\n border: none;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n font-size: 16px;\n padding: 0;\n margin-left: 8px;\n}\n\n.remove-member-btn:hover {\n background-color: #c82333;\n}\n\n#description {\n height: 100px;\n width: 100%;\n border-radius: 20px;\n padding: 10px 0;\n padding-left: 10px;\n resize: none;\n\n}\n\n.role-item-container {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n padding: 5px 0;\n}\n\n.role-item-container span:last-child {\n margin-left: 10px;\n}\n\n.document-card .new-request-btn {\n margin-top: 60%;\n margin-left: 65%;\n padding: 6px 12px;\n background-color: #4CAF50;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 0.9em;\n}\n\n.document-card .new-request-btn:hover {\n background-color: #45a049;\n}\n\n/* Styles pour la modale de nouveau document */\n.modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000;\n}\n\n\n.modal-content-document {\n background: white;\n padding: 20px;\n border-radius: 8px;\n width: 97%;\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n}\n\n.modal-header h2 {\n margin: 0;\n color: #333;\n}\n\n.modal-body {\n margin-bottom: 20px;\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding-top: 20px;\n border-top: 1px solid #eee;\n}\n\n.modal-document {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000;\n}\n.form-group {\n margin-bottom: 15px;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 5px;\n font-weight: bold;\n color: #555;\n}\n\n.form-control {\n width: 100%;\n padding: 8px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n}\n\n.form-row {\n display: flex;\n gap: 15px;\n margin-bottom: 15px;\n}\n\n.form-group.half {\n flex: 1;\n margin-bottom: 0; /* Annule la marge du form-group standard */\n}\n\n.selected-signatories {\n margin: 10px 0;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n min-height: 50px;\n}\n\n.signatory-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 5px;\n margin: 5px 0;\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n.remove-btn {\n background: none;\n border: none;\n color: #dc3545;\n cursor: pointer;\n font-size: 18px;\n padding: 0 5px;\n}\n\n.remove-btn:hover {\n color: #bd2130;\n}\n\n.btn-primary {\n background: #4CAF50;\n color: white;\n padding: 10px 20px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.btn-primary:hover {\n background: #45a049;\n}\n\n.btn-secondary {\n background: #6c757d;\n color: white;\n padding: 8px 15px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.btn-secondary:hover {\n background: #5a6268;\n}\n\n.signatories-list {\n max-height: 300px;\n overflow-y: auto;\n}\n\n.signatory-option {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px;\n border-bottom: 1px solid #eee;\n}\n\n.role-select {\n padding: 4px;\n border: 1px solid #ddd;\n border-radius: 4px;\n margin-left: auto;\n}\n\n.role-section {\n margin-bottom: 20px;\n padding: 10px;\n background: #f8f9fa;\n border-radius: 4px;\n}\n\n.role-section h4 {\n margin: 0 0 10px 0;\n color: #495057;\n}\n\n.members-selection {\n max-height: 300px;\n overflow-y: auto;\n padding: 10px;\n border: 1px solid #dee2e6;\n border-radius: 4px;\n}\n\ninput[type=\"file\"] {\n padding: 10px;\n border: 1px dashed #ccc;\n border-radius: 4px;\n width: 100%;\n margin-top: 5px;\n}\n\n.file-upload-container {\n border: 2px dashed #ccc;\n padding: 20px;\n text-align: center;\n margin: 10px 0;\n border-radius: 5px;\n cursor: pointer;\n}\n\n.file-upload-container:hover {\n background-color: #f5f5f5;\n border-color: #666;\n}\n\n.file-upload-container.dragover {\n background-color: #f0f0f0;\n border-color: #4CAF50;\n}\n\n.file-list {\n margin-top: 10px;\n}\n\n.file-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px;\n margin: 5px 0;\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n.file-info {\n display: flex;\n gap: 10px;\n align-items: center;\n}\n\n.remove-file {\n background: none;\n border: none;\n color: #ff4444;\n cursor: pointer;\n font-size: 18px;\n}\n\n#fileInput {\n display: none;\n}\n\n.required-signatories {\n margin: 10px 0;\n}\n\n.signatory-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px;\n margin: 5px 0;\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n.signatory-item.locked {\n background: #e8e8e8;\n cursor: not-allowed;\n}\n\n.member-name {\n font-weight: 500;\n}\n\n.role-info {\n color: #666;\n font-size: 0.9em;\n}\n\n.lock-icon {\n margin-left: auto;\n opacity: 0.6;\n}\n\n/* Style pour la popup d'alerte */\n.alert-popup {\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background-color: #f44336;\n color: white;\n padding: 15px 25px;\n border-radius: 4px;\n box-shadow: 0 2px 5px rgba(0,0,0,0.2);\n z-index: 1000;\n display: none;\n animation: slideDown 0.3s ease-out;\n}\n\n.sign-button {\n background-color: #4CAF50;\n color: white;\n padding: 8px 16px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 10px;\n}\n\n.sign-button:hover {\n background-color: #45a049;\n}\n\n.sign-button:disabled {\n background-color: #cccccc;\n cursor: not-allowed;\n}\n\n.modal-document {\n background: white;\n border-radius: 8px;\n max-width: 800px;\n width: 90%;\n max-height: 95vh;\n overflow-y: auto;\n position: relative;\n}\n\n.document-details {\n padding: 20px;\n}\n\n.info-section {\n margin: 20px 0;\n background: #f8f9fa;\n padding: 15px;\n border-radius: 6px;\n}\n\n.info-row {\n display: flex;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.label {\n font-weight: bold;\n color: #666;\n}\n\n.description-section {\n margin: 20px 0;\n}\n\n.signatures-section {\n margin: 20px 0;\n}\n\n.signature-item {\n display: flex;\n justify-content: space-between;\n padding: 8px;\n border-bottom: 1px solid #eee;\n}\n\n.signature-item.signed {\n background-color: #e8f5e9;\n}\n\n.signature-item.pending {\n background-color: #fff3e0;\n}\n\n.files-section {\n margin: 20px 0;\n}\n\n.file-item {\n display: flex;\n align-items: center;\n padding: 8px;\n background: #f8f9fa;\n margin-bottom: 5px;\n border-radius: 4px;\n}\n\n.file-icon {\n margin-right: 10px;\n}\n\n.download-link {\n margin-left: auto;\n}\n\n.confirmation-section {\n margin-top: 30px;\n text-align: center;\n}\n\n.warning-text {\n color: #f44336;\n margin-bottom: 15px;\n}\n\n.sign-confirm-btn {\n background-color: #4CAF50;\n color: white;\n padding: 10px 20px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.sign-confirm-btn:hover {\n background-color: #45a049;\n}\n\n.signature-slider-container {\n margin: 20px 20%;\n padding: 10px;\n}\n\n.slider-track {\n position: relative;\n background: #e0e0e0;\n height: 40px;\n border-radius: 20px;\n display: flex;\n align-items: center;\n overflow: hidden;\n}\n\n.slider-handle {\n position: absolute;\n left: 0;\n width: 40px;\n height: 40px;\n background: #4CAF50;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: none;\n z-index: 1;\n}\n\n.slider-arrow {\n color: white;\n font-size: 20px;\n}\n\n.slider-text {\n width: 100%;\n text-align: center;\n color: #666;\n user-select: none;\n}";
// Définir les rôles autorisés
const VISIBILITY_LEVELS = {
PUBLIC: 'public',
CONFIDENTIAL: 'confidential',
PRIVATE: 'private',
};
const DOCUMENT_STATUS = {
DRAFT: 'draft',
PENDING: 'pending'};
const groupsMock = [
{
id: 1,
name: 'Processus 1',
description: 'Description du processus 1',
commonDocuments: [
{
id: 101,
name: 'Règlement intérieur',
description: 'Document vierge pour le règlement intérieur',
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 102,
name: 'Charte de confidentialité',
description: 'Document vierge pour la charte de confidentialité',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 103,
name: 'Procédures générales',
description: 'Document vierge pour les procédures générales',
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 104,
name: 'Urgency A',
description: "Document vierge pour le plan d'urgence A",
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 105,
name: 'Urgency B',
description: "Document vierge pour le plan d'urgence B",
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 106,
name: 'Urgency C',
description: "Document vierge pour le plan d'urgence C",
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 107,
name: 'Document à signer',
description: 'Document vierge pour le règlement intérieur',
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
roles: [
{
name: 'User',
members: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
],
documents: [
{
id: 1,
name: 'Document User A',
description: 'Description du document User A.',
visibility: 'public',
createdAt: '2024-01-01',
deadline: '2024-02-01',
signatures: [
{
member: { id: 1, name: 'Alice' },
signed: true,
signedAt: '2024-01-15',
},
{
member: { id: 2, name: 'Bob' },
signed: false,
},
],
},
{
id: 2,
name: 'Document User B',
description: 'Document vierge pour le rôle User',
visibility: 'confidential',
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 7,
name: 'Document User C',
description: 'Document vierge pour validation utilisateur',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 8,
name: 'Document User D',
description: 'Document vierge pour approbation utilisateur',
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
},
{
name: 'Process',
members: [
{ id: 3, name: 'Charlie' },
{ id: 4, name: 'David' },
],
documents: [
{
id: 3,
name: 'Document Process A',
description: 'Description du document Process A.',
visibility: 'confidential',
createdAt: '2024-01-10',
deadline: '2024-03-01',
signatures: [
{
member: { id: 3, name: 'Charlie' },
signed: true,
signedAt: '2024-01-12',
},
],
},
{
id: 9,
name: 'Document Process B',
description: 'Document vierge pour processus interne',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 10,
name: 'Document Process C',
description: 'Document vierge pour validation processus',
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
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,
},
],
},
],
},
{
name: 'Backup',
members: [
{ id: 15, name: 'Oscar' },
{ id: 16, name: 'Patricia' },
],
documents: [
{
id: 11,
name: 'Document Backup A',
description: 'Document vierge pour sauvegarde',
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
},
],
},
{
id: 2,
name: 'Processus 2',
description: 'Description du processus 2',
commonDocuments: [
{
id: 201,
name: 'Règlement intérieur',
description: 'Document vierge pour le règlement intérieur',
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 202,
name: 'Charte de confidentialité',
description: 'Document vierge pour la charte de confidentialité',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 203,
name: 'Charte de confidentialité',
description: 'Document vierge pour la charte de confidentialité',
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 204,
name: 'Charte de confidentialité',
description: 'Document vierge pour la charte de confidentialité',
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 205,
name: 'Charte de confidentialité',
description: 'Document vierge pour la charte de confidentialité',
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
roles: [
{
name: 'Artefact',
members: [
{ id: 17, name: 'Quinn' },
{ id: 18, name: 'Rachel' },
],
documents: [
{
id: 12,
name: 'Document Artefact A',
description: 'Document vierge pour artefact',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 13,
name: 'Document Artefact B',
description: 'Document vierge pour validation artefact',
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
},
{
name: 'Resolve',
members: [
{ id: 19, name: 'Sam' },
{ id: 20, name: 'Tom' },
],
documents: [
{
id: 14,
name: 'Document Resolve A',
description: 'Document vierge pour résolution',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
},
],
},
{
id: 3,
name: 'Processus 3',
description: 'Description du processus 3',
commonDocuments: [
{
id: 301,
name: 'Règlement intérieur',
description: 'Document vierge pour le règlement intérieur',
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 302,
name: 'Charte de confidentialité',
description: 'Document vierge pour la charte de confidentialité',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 303,
name: 'Procédures générales',
description: 'Document vierge pour les procédures générales',
visibility: VISIBILITY_LEVELS.PUBLIC,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
roles: [
{
name: 'Deposit',
members: [
{ id: 21, name: 'Uma' },
{ id: 22, name: 'Victor' },
],
documents: [
{
id: 15,
name: 'Document Deposit A',
description: 'Document vierge pour dépôt',
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 16,
name: 'Document Deposit B',
description: 'Document vierge pour validation dépôt',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
},
{
name: 'Payment',
members: [
{ id: 23, name: 'Walter' },
{ id: 24, name: 'Xena' },
],
documents: [
{
id: 17,
name: 'Document Payment B',
description: 'Document vierge pour paiement',
visibility: VISIBILITY_LEVELS.PRIVATE,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
{
id: 18,
name: 'Document Payment C',
description: 'Document vierge pour validation paiement',
visibility: VISIBILITY_LEVELS.CONFIDENTIAL,
status: DOCUMENT_STATUS.DRAFT,
createdAt: null,
deadline: null,
signatures: [],
},
],
},
],
},
];
const messagesMock = [
{
memberId: 1,
// Conversations avec Mmber 1
messages: [
{ id: 1, sender: "Mmeber 1", text: "Salut !", time: "10:30 AM" },
{ id: 2, sender: "4NK", text: "Bonjour ! Comment ça va ?", time: "10:31 AM" },
{ id: 3, sender: "4NK", text: "Tout va bien, merci !", time: "10:32 AM" }
]
},
{
memberId: 2,
// Conversations avec Member 2
messages: [
{ id: 1, sender: "Member 2", text: "Salut, on se voit ce soir ?", time: "10:30 AM" },
{ id: 2, sender: "4NK", text: "Oui, à quelle heure ?", time: "10:31 AM" }
]
},
{
memberId: 3,
// Conversations avec Member 3
messages: [
{ id: 1, sender: "Member 3", text: "Hey, ça va ?", time: "10:30 AM" },
{ id: 2, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" }
]
},
{
memberId: 4,
// Conversations avec Member 4
messages: [
{ id: 1, sender: "Member 4", text: "Hey, ça va ?", time: "10:30 AM" },
{ id: 2, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" },
{ id: 3, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" }
]
},
{
memberId: 5,
// Conversations avec Member 5
messages: [
{ id: 1, sender: "Member 5", text: "Hey, ça va ?", time: "10:30 AM" },
{ id: 2, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" },
{ id: 3, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" }
]
},
{
memberId: 6,
// Conversations avec Member 6
messages: [
{ id: 1, sender: "Member 6", text: "Hey, ça va ?", time: "10:30 AM" },
{ id: 2, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" },
{ id: 3, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" }
]
},
{
memberId: 7,
// Conversations avec Member 7
messages: [
{ id: 1, sender: "Member 7", text: "Hey, ça va ?", time: "10:30 AM" },
{ id: 2, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" },
{ id: 3, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" }
]
},
{
memberId: 8,
// Conversations avec Member 8
messages: [
{ id: 1, sender: "Member 8", text: "Hey, ça va ?", time: "10:30 AM" },
{ id: 2, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" },
{ id: 3, sender: "4NK", text: "Ça va et toi ?", time: "10:31 AM" }
]
}
];
const membersMock = [
// Processus 1
{
id: 1,
name: 'Alice',
avatar: 'A',
email: 'alice@company.com',
processRoles: [{ processId: 1, role: 'User' }],
},
{
id: 2,
name: 'Bob',
avatar: 'B',
email: 'bob@company.com',
processRoles: [{ processId: 1, role: 'User' }],
},
{
id: 3,
name: 'Charlie',
avatar: 'C',
email: 'charlie@company.com',
processRoles: [{ processId: 1, role: 'Process' }],
},
{
id: 4,
name: 'David',
avatar: 'D',
email: 'david@company.com',
processRoles: [{ processId: 1, role: 'Process' }],
},
{
id: 15,
name: 'Oscar',
avatar: 'O',
email: 'oscar@company.com',
processRoles: [{ processId: 1, role: 'Backup' }],
},
{
id: 16,
name: 'Patricia',
avatar: 'P',
email: 'patricia@company.com',
processRoles: [{ processId: 1, role: 'Backup' }],
},
// Processus 2
{
id: 17,
name: 'Quinn',
avatar: 'Q',
email: 'quinn@company.com',
processRoles: [{ processId: 2, role: 'Artefact' }],
},
{
id: 18,
name: 'Rachel',
avatar: 'R',
email: 'rachel@company.com',
processRoles: [{ processId: 2, role: 'Artefact' }],
},
{
id: 19,
name: 'Sam',
avatar: 'S',
email: 'sam@company.com',
processRoles: [{ processId: 2, role: 'Resolve' }],
},
{
id: 20,
name: 'Tom',
avatar: 'T',
email: 'tom@company.com',
processRoles: [{ processId: 2, role: 'Resolve' }],
},
// Processus 3
{
id: 21,
name: 'Uma',
avatar: 'U',
email: 'uma@company.com',
processRoles: [{ processId: 3, role: 'Deposit' }],
},
{
id: 22,
name: 'Victor',
avatar: 'V',
email: 'victor@company.com',
processRoles: [{ processId: 3, role: 'Deposit' }],
},
{
id: 23,
name: 'Walter',
avatar: 'W',
email: 'walter@company.com',
processRoles: [{ processId: 3, role: 'Payment' }],
},
{
id: 24,
name: 'Xena',
avatar: 'X',
email: 'xena@company.com',
processRoles: [{ processId: 3, role: 'Payment' }],
},
];
class MessageStore {
STORAGE_KEY = "chat_messages";
messages = [];
constructor() {
this.messages = this.loadFromLocalStorage() || [];
}
loadFromLocalStorage() {
try {
const stored = localStorage.getItem(this.STORAGE_KEY);
return stored ? JSON.parse(stored) : null;
} catch (error) {
console.error("Error loading messages:", error);
return null;
}
}
getMessages() {
return this.messages;
}
setMessages(messages) {
this.messages = messages;
this.saveToLocalStorage();
}
saveToLocalStorage() {
try {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.messages));
} catch (error) {
console.error("Error saving messages:", error);
}
}
addMessage(memberId, message) {
const memberMessages = this.messages.find((m) => String(m.memberId) === String(memberId));
if (memberMessages) {
memberMessages.messages.push(message);
} else {
this.messages.push({
memberId: String(memberId),
messages: [message]
});
}
this.saveToLocalStorage();
}
}
const messageStore = new MessageStore();
let currentUser = membersMock[0];
class SignatureElement extends HTMLElement {
selectedMemberId = null;
messagesMock = [];
dom;
notifications = [];
notificationBadge = document.querySelector(".notification-badge");
notificationBoard = document.getElementById("notification-board");
notificationBell = document.getElementById("notification-bell");
selectedSignatories = [];
allMembers = membersMock.map((member) => ({
id: member.id,
name: member.name,
roleName: "Default Role"
}));
showAlert(message) {
let alertPopup = this.shadowRoot?.querySelector(".alert-popup");
if (!alertPopup) {
alertPopup = document.createElement("div");
alertPopup.className = "alert-popup";
this.shadowRoot?.appendChild(alertPopup);
}
alertPopup.textContent = message;
alertPopup.style.display = "block";
setTimeout(() => {
alertPopup.style.display = "none";
}, 3e3);
}
signDocument(documentId, processId, isCommonDocument = false) {
try {
let initDrag = function(e) {
isDragging = true;
startX = "touches" in e ? e.touches[0].clientX : e.clientX;
sliderLeft = slider?.offsetLeft || 0;
}, drag = function(e) {
if (!isDragging || !slider || !sliderTrack)
return;
e.preventDefault();
const x = "touches" in e ? e.touches[0].clientX : e.clientX;
const deltaX = x - startX;
let newLeft = sliderLeft + deltaX;
const maxLeft = sliderTrack.offsetWidth - slider.offsetWidth;
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
slider.style.left = `${newLeft}px`;
if (newLeft > maxLeft * 0.9) {
stopDrag(e);
this.confirmSignature(documentId, processId, isCommonDocument);
}
}, stopDrag = function(e) {
if (!isDragging || !slider)
return;
isDragging = false;
if (slider.offsetLeft < (sliderTrack?.offsetWidth || 0) * 0.9) {
slider.style.left = "0px";
}
};
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) => g.id === processId);
if (!group) {
throw new Error("Process not found");
}
let targetDoc;
if (isCommonDocument) {
targetDoc = group.commonDocuments.find((d) => d.id === documentId);
} else {
for (const role of group.roles) {
if (role.documents) {
targetDoc = role.documents.find((d) => d.id === documentId);
if (targetDoc)
break;
}
}
}
if (!targetDoc) {
throw new Error("Document not found");
}
const canSign = isCommonDocument ? targetDoc.signatures?.some((sig) => sig.member?.name === currentUser?.name && !sig.signed) : this.canUserSignDocument(targetDoc, currentUser?.name, currentUser);
if (!canSign) {
this.showAlert("You do not have the necessary rights to sign this document.");
return;
}
const modalHtml = `
<div class="modal-overlay" id="signatureModal">
<div class="modal-document">
<div class="modal-content-document">
<div class="details-header">
<h2>Signature du document</h2>
<button class="close-btn" onclick="closeModal(this)">×</button>
</div>
<div class="document-details">
<h3>${targetDoc.name}</h3>
<div class="info-section">
<div class="info-row">
<span class="label">Created:</span>
<span class="value">${new Date(targetDoc.createdAt).toLocaleDateString()}</span>
</div>
<div class="info-row">
<span class="label">Deadline:</span>
<span class="value">${new Date(targetDoc.deadline).toLocaleDateString()}</span>
</div>
<div class="info-row">
<span class="label">Visibility:</span>
<span class="value">${targetDoc.visibility}</span>
</div>
</div>
<div class="description-section">
<h4>Description:</h4>
<p>${targetDoc.description || "No description available"}</p>
</div>
<div class="signatures-section">
<h4>Signatures status:</h4>
<div class="signatures-list">
${targetDoc.signatures.map((sig) => `
<div class="signature-item ${sig.signed ? "signed" : "pending"}">
<span class="signer-name">${sig.member.name}</span>
<span class="signature-status">
${sig.signed ? `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : "unknown date"}` : "⌛ Pending"}
</span>
</div>
`).join("")}
</div>
</div>
${this.getFileList().length > 0 ? `
<div class="files-section">
<h4>Files attached:</h4>
<div class="files-list">
${this.getFileList().map((file) => `
<div class="file-item">
<span class="file-icon"></span>
<span class="file-name">${file.name}</span>
<a href="${file.url}" class="download-link" download="${file.name}">
<span class="download-icon">⬇</span>
</a>
</div>
`).join("")}
</div>
</div>
` : ""}
<div class="confirmation-section">
<p class="warning-text">By signing this document, you confirm that you have read its contents.</p>
<div class="signature-slider-container">
<div class="slider-track">
<div class="slider-handle" id="signatureSlider" draggable="true">
<span class="slider-arrow">➜</span>
</div>
<span class="slider-text">Drag to sign</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>`;
const modalElement = document.createElement("div");
modalElement.className = "modal-overlay";
modalElement.innerHTML = modalHtml;
this.shadowRoot?.appendChild(modalElement);
const slider = modalElement.querySelector("#signatureSlider");
const sliderTrack = slider?.parentElement;
let isDragging = false;
let startX;
let sliderLeft;
if (slider && sliderTrack) {
slider.addEventListener("mousedown", (e) => initDrag(e));
slider.addEventListener("touchstart", (e) => initDrag(e));
modalElement.addEventListener("mousemove", (e) => drag.call(this, e));
modalElement.addEventListener("touchmove", (e) => drag.call(this, e));
modalElement.addEventListener("mouseup", (e) => stopDrag(e));
modalElement.addEventListener("touchend", (e) => stopDrag(e));
}
} catch (error) {
console.error("Error displaying modal:", error);
this.showAlert(error instanceof Error ? error.message : "Error displaying modal");
}
}
constructor() {
super();
this.attachShadow({ mode: "open" });
this.dom = getCorrectDOM("signature-element");
this.shadowRoot.innerHTML = `
<style>
${signatureStyle}
</style>
<div class="container">
<!-- List of groups -->
<div class="group-list">
<ul id="group-list">
</ul>
</div>
<!-- Chat area -->
<div class="chat-area">
<div class="chat-header" id="chat-header">
<!-- Chat title -->
</div>
<div class="messages" id="messages">
<!-- Messages -->
</div>
<!-- Input area -->
<div class="input-area">
<label for="file-input" class="attachment-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M13.514 2.444l-10.815 10.785c-.449.449-.678 1.074-.625 1.707l.393 4.696c.041.479.422.86.9.9l4.697.394c.633.053 1.258-.177 1.707-.626l11.875-11.844c.196-.196.195-.512 0-.707l-3.536-3.536c-.195-.195-.511-.196-.707 0l-8.878 8.848c-.162.162-.253.382-.253.611v.725c0 .184.148.332.332.332h.725c.229 0 .448-.092.61-.254l7.11-7.08 1.415 1.415-7.386 7.354c-.375.375-.885.586-1.414.586h-2.414c-.555 0-1-.448-1-1v-2.414c0-.53.211-1.039.586-1.414l9.506-9.477c.781-.781 2.049-.781 2.829-.001l4.243 4.243c.391.391.586.902.586 1.414 0 .512-.196 1.025-.587 1.416l-12.35 12.319c-.748.747-1.76 1.164-2.81 1.164-.257 0-6.243-.467-6.499-.487-.664-.052-1.212-.574-1.268-1.267-.019-.242-.486-6.246-.486-6.499 0-1.05.416-2.062 1.164-2.811l10.936-10.936 1.414 1.444z"/>
</svg>
</label>
<input type="file" id="file-input" style="display: none;" />
<textarea id="message-input" rows="3" placeholder="Type your message..."></textarea>
<button id="send-button">Send</button>
</div>
</div>
</div>
`;
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);
document.addEventListener("click", (event) => {
if (this.notificationBoard && this.notificationBoard.style.display === "block" && !this.notificationBoard.contains(event.target) && this.notificationBell && !this.notificationBell.contains(event.target)) {
this.notificationBoard.style.display = "none";
}
});
this.initMessageEvents();
this.initFileUpload();
}
initMessageEvents() {
const sendButton = this.shadowRoot?.getElementById("send-button");
if (sendButton) {
sendButton.addEventListener("click", () => this.sendMessage());
}
const messageInput = this.shadowRoot?.getElementById("message-input");
if (messageInput) {
messageInput.addEventListener("keypress", (event) => {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
this.sendMessage();
}
});
}
}
initFileUpload() {
const fileInput = this.shadowRoot?.getElementById("file-input");
if (fileInput) {
fileInput.addEventListener("change", (event) => {
const target = event.target;
if (target.files && target.files.length > 0) {
this.sendFile(target.files[0]);
}
});
}
}
calculateDuration(startDate, endDate) {
const start = new Date(startDate || "");
const end = new Date(endDate || "");
const duration = end.getTime() - start.getTime();
return Math.floor(duration / (1e3 * 60 * 60 * 24));
}
// Add this helper function
canUserAccessDocument(document2, roleId, currentUserRole) {
if (document2.visibility === "public") {
return true;
}
return roleId === currentUserRole;
}
canUserSignDocument(document2, role, user) {
console.log("Checking signing rights for:", {
document: document2,
role,
user,
userRoles: user.processRoles
});
const isSignatory = document2.signatures?.some((sig) => 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;
}
return true;
}
closeProcessDetails(groupId) {
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
removeNotification(index) {
this.notifications?.splice(index, 1);
this.renderNotifications();
this.updateNotificationBadge();
}
// Show notifications
renderNotifications() {
if (!this.notificationBoard)
return;
this.notificationBoard.innerHTML = "";
if (this.notifications.length === 0) {
this.notificationBoard.innerHTML = '<div class="no-notification">No notifications available</div>';
return;
}
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);
});
}
updateNotificationBadge() {
if (!this.notificationBadge)
return;
const count = this.notifications.length;
this.notificationBadge.textContent = count > 99 ? "+99" : count.toString();
this.notificationBadge.style.display = count > 0 ? "block" : "none";
}
// Add notification
addNotification(memberId, message) {
const notification = {
memberId,
text: `New message from Member ${memberId}: ${message.text}`,
time: message.time
};
this.notifications.push(notification);
this.renderNotifications();
this.updateNotificationBadge();
}
// Send a messsage
sendMessage() {
const messageInput = this.shadowRoot?.getElementById("message-input");
if (!messageInput)
return;
const messageText = messageInput.value.trim();
if (messageText === "" || this.selectedMemberId === null) {
return;
}
const newMessage = {
id: Date.now(),
sender: "4NK",
text: messageText,
time: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
type: "text"
};
messageStore.addMessage(this.selectedMemberId, newMessage);
this.messagesMock = messageStore.getMessages();
this.loadMemberChat(this.selectedMemberId);
messageInput.value = "";
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);
}
}, 2e3);
}
showProcessDetails(group, groupId) {
console.log("Showing details for group:", groupId);
const allDetailsAreas = this.shadowRoot?.querySelectorAll(".process-details");
if (allDetailsAreas) {
allDetailsAreas.forEach((area) => {
area.style.display = "none";
});
}
const container = this.shadowRoot?.querySelector(".container");
if (!container) {
console.error("Container not found");
return;
}
const storedGroups = JSON.parse(localStorage.getItem("groups") || "[]");
const storedGroup = storedGroups.find((g) => g.id === groupId);
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 = `
<div class="process-details-header">
<h2>${displayGroup.name}</h2>
<div class="header-buttons">
</div>
</div>
<div class="process-details-content">
<div class="details-section">
<h3>Description</h3>
<p>${displayGroup.description || "No description available"}</p>
</div>
<div class="details-section">
<h3>Documents Communs</h3>
<div class="documents-grid">
${displayGroup.commonDocuments.map((document2) => {
const totalSignatures = document2.signatures?.length || 0;
const signedCount = document2.signatures?.filter((sig) => sig.signed).length || 0;
const percentage = totalSignatures > 0 ? signedCount / totalSignatures * 100 : 0;
const isVierge = !document2.createdAt || !document2.deadline || !document2.signatures?.length;
const canSign = document2.signatures?.some((sig) => sig.member && "id" in sig.member && sig.member.id === currentUser.id && !sig.signed);
const signButton = !isVierge ? `
${totalSignatures > 0 && signedCount < totalSignatures && canSign ? `
<button class="sign-button" onclick="signDocument(${document2.id}, ${groupId}, true)">
Sign the document
</button>
` : ""}
` : "";
return `
<div class="document-card ${document2.visibility} ${isVierge ? "vierge" : ""}">
<div class="document-header">
<h4>${isVierge ? `⚠️ ${document2.name}` : document2.name}</h4>
<span class="document-visibility">${document2.visibility}</span>
</div>
<div class="document-info">
${!isVierge ? `
<p class="document-date">Created on: ${document2.createdAt ? new Date(document2.createdAt).toLocaleDateString() : "N/A"}</p>
<p class="document-deadline">Deadline: ${document2.deadline ? new Date(document2.deadline).toLocaleDateString() : "N/A"}</p>
<p class="document-duration">Duration: ${this.calculateDuration(document2.createdAt || "", document2.deadline || "")} days</p>
<div class="document-signatures">
<h5>Signatures:</h5>
<div class="signatures-list">
${document2.signatures?.map((sig) => `
<div class="signature-item ${sig.signed ? "signed" : "pending"}">
<span class="signer-name">${sig.member.name}</span>
<span class="signature-status">
${sig.signed ? `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : "unknown date"}` : "⌛ Pending"}
</span>
</div>
`).join("")}
</div>
<div class="progress-bar">
<div class="progress" style="width: ${percentage}%;"></div>
</div>
<p>${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)</p>
</div>
` : `
<p>Document vierge - Waiting for creation</p>
<button class="new-request-btn" onclick="newRequest({
processId: ${displayGroup.id},
processName: '${displayGroup.name}',
roleId: 0,
roleName: 'common',
documentId: ${document2.id},
documentName: '${document2.name}'
})">New request</button>
`}
${signButton}
</div>
</div>
`;
}).join("")}
</div>
</div>
<div class="details-section">
<h3>Roles and Documents</h3>
${displayGroup.roles.map((role) => {
const accessibleDocuments = (role.documents || []).filter((doc) => this.canUserAccessDocument(doc, role.name, currentUser.processRoles?.[0]?.role || ""));
return `
<div class="role-section">
<h4>${role.name}</h4>
<div class="documents-grid">
${accessibleDocuments.map((document2) => {
const isVierge = !document2.createdAt || !document2.deadline || document2.signatures.length === 0;
const canSign = this.canUserSignDocument(document2, role.name, currentUser);
const signButton = !isVierge ? `
${document2.signatures.length > 0 && document2.signatures.filter((sig) => sig.signed).length < document2.signatures.length && canSign ? `
<button class="sign-button" onclick="signDocument(${document2.id}, ${groupId}, false)">
Sign the document
</button>
` : ""}
` : "";
return `
<div class="document-card ${document2.visibility} ${isVierge ? "vierge" : ""}">
<div class="document-header">
<h4>${isVierge ? `⚠️ ${document2.name}` : document2.name}</h4>
<span class="document-visibility">${document2.visibility}</span>
</div>
<div class="document-info">
${!isVierge ? `
<p class="document-date">Created on: ${document2.createdAt ? new Date(document2.createdAt).toLocaleDateString() : "N/A"}</p>
<p class="document-deadline">Deadline: ${document2.deadline ? new Date(document2.deadline).toLocaleDateString() : "N/A"}</p>
<p class="document-duration">Duration: ${this.calculateDuration(document2.createdAt || "", document2.deadline || "")} days</p>
` : "<p>Document vierge - Waiting for creation</p>"}
</div>
${!isVierge ? `
<div class="document-signatures">
<h5>Signatures:</h5>
<div class="signatures-list">
${document2.signatures.map((sig) => `
<div class="signature-item ${sig.signed ? "signed" : "pending"}">
<span class="signer-name">${sig.member.name}</span>
<span class="signature-status">
${sig.signed ? `✓ Signé le ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : "date inconnue"}` : "⌛ En attente"}
</span>
</div>
`).join("")}
</div>
<div class="progress-bar">
<div class="progress" style="width: ${document2.signatures.filter((sig) => sig.signed).length / document2.signatures.length * 100}%;"></div>
</div>
<p>${document2.signatures.filter((sig) => sig.signed).length} out of ${document2.signatures.length} signed (${(document2.signatures.filter((sig) => sig.signed).length / document2.signatures.length * 100).toFixed(0)}%)</p>
</div>
` : ""}
${signButton}
</div>
`;
}).join("")}
</div>
</div>
`;
}).join("")}
</div>
<div class="details-section">
<h3>Members by Role</h3>
<div class="roles-grid">
${displayGroup.roles.map((role) => `
<div class="role-block">
<h4>${role.name}</h4>
<ul class="members-list">
${role.members.map((member) => `
<li onclick="loadMemberChat(${member.id})">${member.name}</li>
`).join("")}
</ul>
</div>
`).join("")}
</div>
</div>
`;
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
scrollToBottom(container) {
container.scrollTop = container.scrollHeight;
}
// Load the list of members
loadMemberChat(memberId) {
this.selectedMemberId = String(memberId);
const memberMessages = this.messagesMock.find((m) => String(m.memberId) === String(memberId));
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) => {
const messageElement = document.createElement("div");
messageElement.className = "message-container";
const messageContent = document.createElement("div");
messageContent.className = "message";
if (message.type === "file") {
messageContent.innerHTML = `<a href="${message.fileData}" download="${message.fileName}" target="_blank">${message.fileName}</a>`;
messageContent.classList.add("user");
} else {
messageContent.innerHTML = `<strong>${message.sender}</strong>: ${message.text} <span style="float: right;">${message.time}</span>`;
if (message.sender === "4NK") {
messageContent.classList.add("user");
}
}
messageElement.appendChild(messageContent);
messagesContainer.appendChild(messageElement);
});
}
this.scrollToBottom(messagesContainer);
}
toggleMembers(role, roleElement) {
let memberList = roleElement.querySelector(".member-list");
if (memberList) {
memberList.style.display = memberList.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
toggleRoles(group, groupElement) {
console.log("=== toggleRoles START ===");
console.log("Group:", group.name);
console.log("Group roles:", group.roles);
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);
let container = roleItem.querySelector(".role-item-container");
if (!container) {
container = document.createElement("div");
container.className = "role-item-container";
const nameSpan = document.createElement("span");
nameSpan.className = "role-name";
nameSpan.textContent = roleItem.textContent?.trim() || "";
container.appendChild(nameSpan);
roleItem.textContent = "";
roleItem.appendChild(container);
}
const roleName = roleItem.textContent?.trim();
console.log("Role name from textContent:", roleName);
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.style.display = roleList.style.display === "none" ? "block" : "none";
}
}
loadGroupList() {
const groupList = this.shadowRoot?.getElementById("group-list");
if (!groupList)
return;
groupsMock.forEach((group) => {
const li = document.createElement("li");
li.className = "group-list-item";
const container = document.createElement("div");
container.className = "group-item-container";
const nameSpan = document.createElement("span");
nameSpan.textContent = group.name;
nameSpan.className = "process-name";
nameSpan.addEventListener("click", (event) => {
event.stopPropagation();
this.toggleRoles(group, li);
});
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);
};
container.appendChild(nameSpan);
container.appendChild(settingsIcon);
li.appendChild(container);
const roleList = document.createElement("ul");
roleList.className = "role-list";
roleList.style.display = "none";
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
toggleUserList() {
const userList = getCorrectDOM("userList");
if (!userList)
return;
if (!userList.classList.contains("show")) {
userList.innerHTML = membersMock.map((member) => `
<div class="user-list-item" onclick="switchUser('${member.id}')">
<span class="user-avatar">${member.avatar}</span>
<div>
<span class="user-name">${member.name}</span>
<span class="user-email">${member.email}</span>
</div>
</div>
`).join("");
}
userList.classList.toggle("show");
}
switchUser(userId) {
const user = membersMock.find((member) => member.id === userId);
if (!user)
return;
currentUser = user;
this.updateCurrentUserDisplay();
const userList = getCorrectDOM("userList");
userList?.classList.remove("show");
}
// Function to update the display of the current user
updateCurrentUserDisplay() {
const userDisplay = getCorrectDOM("current-user");
if (userDisplay) {
userDisplay.innerHTML = `
<div class="current-user-info">
<span class="user-avatar">${currentUser.avatar}</span>
<span class="user-name">${currentUser.name}</span>
</div>
`;
}
}
// Generate an automatic response
generateAutoReply(senderName) {
return {
id: Date.now(),
sender: senderName,
text: "OK...",
time: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
type: "text"
};
}
// Send a file
sendFile(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,
fileData,
time: (/* @__PURE__ */ 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
fileList = this.shadowRoot?.getElementById("fileList");
getFileList() {
const files = Array.from(this.fileList?.querySelectorAll(".file-item") || []).map((fileItem) => {
const fileName = fileItem.querySelector(".file-name")?.textContent || "";
return {
name: fileName,
url: fileItem.dataset.content || "#"
};
});
return files;
}
// New function to display the documents of a role
showRoleDocuments(role, group) {
const storedGroups = JSON.parse(localStorage.getItem("groups") || "[]");
const storedGroup = storedGroups.find((g) => g.id === group.id);
const storedRole = storedGroup?.roles.find((r) => r.name === role.name);
const displayRole = storedRole || role;
console.log("Showing documents for role:", displayRole.name, "in group:", group.name);
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;
}
const detailsArea = document.createElement("div");
detailsArea.id = `role-documents-${displayRole.name}`;
detailsArea.className = "process-details";
const accessibleDocuments = (displayRole.documents || []).filter((doc) => this.canUserAccessDocument(doc, displayRole.name, currentUser.processRoles?.[0]?.role || ""));
detailsArea.innerHTML = `
<div class="modal-content-document">
<div class="details-header">
<h2>${displayRole.name} Documents</h2>
<div class="header-buttons">
<button class="close-btn" onclick="closeRoleDocuments('${displayRole.name}')">✕</button>
</div>
</div>
<div class="process-details-content">
<div class="details-section">
<h3>Documents</h3>
<div class="documents-grid">
${accessibleDocuments.map((document2) => {
const totalSignatures = document2.signatures.length;
const signedCount = document2.signatures.filter((sig) => sig.signed).length;
const percentage = totalSignatures > 0 ? signedCount / totalSignatures * 100 : 0;
const isVierge = !document2.createdAt || !document2.deadline || document2.signatures.length === 0;
this.canUserSignDocument(document2, role.name, currentUser);
return `
<div class="document-card ${document2.visibility} ${isVierge ? "vierge" : ""}">
<div class="document-header">
<h4>${isVierge ? `⚠️ ${document2.name}` : document2.name}</h4>
<span class="document-visibility">${document2.visibility}</span>
</div>
<div class="document-info">
${!isVierge ? `
<p class="document-date">Created on: ${document2.createdAt ? new Date(document2.createdAt).toLocaleDateString() : "N/A"}</p>
<p class="document-deadline">Deadline: ${document2.deadline ? new Date(document2.deadline).toLocaleDateString() : "N/A"}</p>
<p class="document-duration">Duration: ${this.calculateDuration(document2.createdAt, document2.deadline)} days</p>
<div class="document-signatures">
<h5>Signatures:</h5>
<div class="signatures-list">
${document2.signatures.map((sig) => `
<div class="signature-item ${sig.signed ? "signed" : "pending"}">
<span class="signer-name">${sig.member.name}</span>
<span class="signature-status">
${sig.signed ? `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : "unknown date"}` : "⌛ Pending"}
</span>
</div>
`).join("")}
</div>
<div class="progress-bar">
<div class="progress" style="width: ${percentage}%;"></div>
</div>
<p>${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)</p>
</div>
` : `
<p>Blank document - Waiting for creation</p>
${this.canUserAccessDocument(document2, displayRole.name, currentUser.processRoles?.[0]?.role || "") ? `
<button class="new-request-btn" onclick="newRequest({
processId: ${group.id},
processName: '${group.name}',
roleId: ${role.id},
roleName: '${role.name}',
documentId: ${document2.id},
documentName: '${document2.name}'
})">New request</button>
` : ""}
`}
</div>
</div>
`;
}).join("")}
</div>
</div>
</div>
</div>
`;
container.appendChild(detailsArea);
}
// Function to close the documents view of a role
closeRoleDocuments(roleName) {
const detailsArea = this.shadowRoot?.getElementById(`role-documents-${roleName}`);
if (detailsArea) {
detailsArea.remove();
}
}
handleFiles(files, fileList) {
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 = `
<div class="file-info">
<span class="file-name">${file.name}</span>
<span class="file-size">(${(file.size / 1024).toFixed(1)} KB)</span>
</div>
<button type="button" class="remove-file">×</button>
`;
fileItem.dataset.content = fileContent;
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
newRequest(params) {
if (!params || !params.processId) {
console.error("Paramètres invalides:", params);
this.showAlert("Invalid parameters for new request");
return;
}
const modal = document.createElement("div");
modal.className = "modal-overlay";
const process = groupsMock.find((g) => g.id === params.processId);
if (!process) {
console.error("Processus non trouvé:", params.processId);
this.showAlert("Process not found");
return;
}
let membersToDisplay = [];
try {
if (params.roleName === "common") {
membersToDisplay = process.roles.reduce((members, 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);
this.showAlert("Error retrieving members");
return;
}
modal.innerHTML = `
<div class="modal-document">
<div class="modal-content-document">
<div class="details-header">
<h2>New Document Request</h2>
<span class="document-context">
Process: ${params.processName} | Role: ${params.roleName} | Document: ${params.documentName}
</span>
</div>
<form id="newDocumentForm" class="document-form">
<input type="hidden" id="processId" value="${params.processId}">
<input type="hidden" id="roleId" value="${params.roleId}">
<input type="hidden" id="documentId" value="${params.documentId}">
<div class="form-left">
<div class="form-row">
<div class="form-group half">
<label for="documentName">Document Name*:</label>
<input type="text" id="documentName" required value="${params.documentName}">
</div>
<div class="form-group half">
<label for="createdAt">Created At:</label>
<input type="text" id="createdAt" value="${(/* @__PURE__ */ new Date()).toLocaleDateString()}" readonly>
</div>
</div>
<div class="form-group">
<label for="description">Description:</label>
<textarea id="description"></textarea>
</div>
<div class="form-row">
<div class="form-group half">
<label for="visibility">Visibility*:</label>
<select id="visibility" required>
<option value="public">Public</option>
<option value="confidential">Confidential</option>
<option value="private">Private</option>
</select>
</div>
<div class="form-group half">
<label for="deadline">Deadline:</label>
<input type="date"
id="deadline"
min="${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}"
required>
</div>
</div>
<div class="form-group">
<label>Import Files</label>
<div class="file-upload-container" id="dropZone">
<input type="file" id="fileInput" multiple accept=".pdf,.doc,.docx,.txt">
<p>Drop files here or click to select files</p>
</div>
<div id="fileList" class="file-list"></div>
</div>
<div class="form-group">
<label>Required Signatories:</label>
<div class="required-signatories">
${membersToDisplay.map((member) => `
<div class="signatory-item">
<span class="member-name">${member.name}</span>
<span class="role-info">${member.roleName}</span>
</div>
`).join("")}
</div>
</div>
</div>
</form>
<div class="modal-footer">
<button class="cancel-btn" onclick="closeModal(this)">Cancel</button>
${params.roleName === "common" ? '<button class="confirm-btn" onclick="submitCommonDocument(event)">Request</button>' : '<button class="confirm-btn" onclick="submitNewDocument(event)">Request</button>'}
</div>
</div>
</div>
`;
this.shadowRoot?.appendChild(modal);
const dropZone = modal.querySelector("#dropZone");
const fileInput = modal.querySelector("#fileInput");
const fileList = modal.querySelector("#fileList");
dropZone.addEventListener("click", () => {
fileInput.click();
});
fileInput.addEventListener("change", (e) => {
const target = e.target;
if (target.files && target.files.length > 0) {
this.handleFiles(target.files, fileList);
}
});
dropZone.addEventListener("dragover", (e) => {
e.preventDefault();
dropZone.classList.add("dragover");
});
dropZone.addEventListener("dragleave", () => {
dropZone.classList.remove("dragover");
});
dropZone.addEventListener("drop", (e) => {
e.preventDefault();
dropZone.classList.remove("dragover");
if (e.dataTransfer?.files) {
this.handleFiles(e.dataTransfer.files, fileList);
}
});
}
closeModal(button) {
const modalOverlay = button.closest(".modal-overlay");
if (modalOverlay) {
modalOverlay.remove();
}
}
submitNewDocument(event) {
event.preventDefault();
const form = this.shadowRoot?.getElementById("newDocumentForm");
if (!form) {
this.showAlert("Form not found");
return;
}
const fileList = this.shadowRoot?.getElementById("fileList");
const files = Array.from(fileList?.querySelectorAll(".file-item") || []).map((fileItem) => {
const fileName = fileItem.querySelector(".file-name")?.textContent || "";
return {
name: fileName,
url: fileItem.dataset.content || "#"
};
});
const processId = Number(form.querySelector("#processId")?.value);
const documentId = Number(form.querySelector("#documentId")?.value);
const documentName = form.querySelector("#documentName")?.value?.trim();
const description = form.querySelector("#description")?.value?.trim();
const deadline = form.querySelector("#deadline")?.value;
const visibility = form.querySelector("#visibility")?.value;
if (!documentName || !description || !deadline) {
this.showAlert("Please fill in all required fields");
return;
}
try {
const groups = JSON.parse(localStorage.getItem("groups") || JSON.stringify(groupsMock));
const group = groups.find((g) => g.id === processId);
if (!group) {
this.showAlert("Process not found");
return;
}
const role = group.roles.find((r) => r.documents?.some((d) => d.id === documentId));
if (!role) {
this.showAlert("Role not found");
return;
}
const updatedDocument = {
id: documentId,
name: documentName,
description,
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
deadline,
visibility,
status: "pending",
signatures: role.members.map((member) => ({
member,
signed: false,
signedAt: null
})),
files
// Ajout des fichiers au document
};
const documentIndex = role.documents.findIndex((d) => d.id === documentId);
if (documentIndex !== -1) {
role.documents[documentIndex] = updatedDocument;
}
localStorage.setItem("groups", JSON.stringify(groups));
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: void 0
};
}
}
}
if (event.target instanceof HTMLElement) {
this.closeModal(event.target);
}
this.showRoleDocuments(role, group);
this.showAlert("Document updated successfully!");
} catch (error) {
console.error("Error saving:", error);
this.showAlert("An error occurred while saving");
}
}
submitCommonDocument(event) {
event.preventDefault();
const form = this.shadowRoot?.getElementById("newDocumentForm");
if (!form) {
this.showAlert("Form not found");
return;
}
const processId = Number(form.querySelector("#processId")?.value);
const documentId = Number(form.querySelector("#documentId")?.value);
const documentName = form.querySelector("#documentName")?.value?.trim();
const description = form.querySelector("#description")?.value?.trim();
const deadline = form.querySelector("#deadline")?.value;
const visibility = form.querySelector("#visibility")?.value;
if (!documentName || !description || !deadline) {
this.showAlert("Please fill in all required fields");
return;
}
try {
const groups = JSON.parse(localStorage.getItem("groups") || JSON.stringify(groupsMock));
const group = groups.find((g) => g.id === processId);
if (!group) {
this.showAlert("Process not found");
return;
}
const allMembers = group.roles.reduce((acc, role) => {
return acc.concat(role.members);
}, []);
const fileList = this.shadowRoot?.getElementById("fileList");
const files = Array.from(fileList?.querySelectorAll(".file-item") || []).map((fileItem) => {
const fileName = fileItem.querySelector(".file-name")?.textContent || "";
return {
name: fileName,
url: fileItem.dataset.content || "#"
};
});
const updatedDocument = {
id: documentId,
name: documentName,
description,
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
deadline,
visibility,
status: "pending",
signatures: allMembers.map((member) => ({
member,
signed: false,
signedAt: null
})),
files
};
const documentIndex = group.commonDocuments.findIndex((d) => 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);
this.showAlert("Document common updated successfully!");
} catch (error) {
console.error("Error saving:", error);
this.showAlert("An error occurred while saving");
}
}
submitRequest() {
this.showAlert("Request submitted!");
}
closeNewRequest() {
const newRequestView = document.getElementById("new-request-view");
if (newRequestView) {
newRequestView.style.display = "none";
newRequestView.remove();
}
}
submitDocumentRequest(documentId) {
const createdAt = this.shadowRoot?.getElementById("createdAt")?.value || "";
const deadline = this.shadowRoot?.getElementById("deadline")?.value || "";
const visibility = this.shadowRoot?.getElementById("visibility")?.value || "";
const description = this.shadowRoot?.getElementById("description")?.value || "";
const selectedMembers = Array.from(this.shadowRoot?.querySelectorAll('input[name="selected-members"]:checked') || []).map((checkbox) => checkbox.value);
if (!createdAt || !deadline || selectedMembers.length === 0) {
this.showAlert("Please fill in all required fields and select at least one member.");
return;
}
console.log("Document submission:", {
documentId,
createdAt,
deadline,
visibility,
description,
selectedMembers
});
this.showAlert("Document request submitted successfully!");
this.closeNewRequest();
}
// FUNCTIONS FOR SIGNATURE
// New function to confirm the signature
confirmSignature(documentId, processId, isCommonDocument) {
try {
console.log("Current user:", currentUser);
const groups = JSON.parse(localStorage.getItem("groups") || JSON.stringify(groupsMock));
const group = groups.find((g) => g.id === processId);
if (!group) {
throw new Error("Process not found");
}
let targetDoc;
if (isCommonDocument) {
targetDoc = group.commonDocuments.find((d) => d.id === documentId);
} else {
for (const role of group.roles) {
if (role.documents) {
targetDoc = role.documents.find((d) => d.id === documentId);
if (targetDoc)
break;
}
}
}
if (!targetDoc) {
throw new Error("Document not found");
}
const userSignature = targetDoc.signatures.find((sig) => 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 = (/* @__PURE__ */ new Date()).toISOString();
localStorage.setItem("groups", JSON.stringify(groups));
const modalOverlay = this.shadowRoot?.querySelector(".modal-overlay");
if (modalOverlay) {
modalOverlay.remove();
}
if (isCommonDocument) {
this.showProcessDetails(group, processId);
} else {
const role = group.roles.find((r) => r.documents?.includes(targetDoc));
if (role) {
this.showRoleDocuments(role, group);
}
}
this.showAlert("Document signed successfully!");
} catch (error) {
console.error("Error signing document:", error);
this.showAlert(error instanceof Error ? error.message : "Error signing document");
}
}
initializeEventListeners() {
document.addEventListener("DOMContentLoaded", () => {
const newRequestBtn = this.shadowRoot?.getElementById("newRequestBtn");
if (newRequestBtn) {
newRequestBtn.addEventListener("click", () => {
this.newRequest({
processId: 0,
processName: "",
roleId: 0,
roleName: "",
documentId: 0,
documentName: ""
});
});
}
});
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) => {
if (event.key === "Enter") {
event.preventDefault();
this.sendMessage();
}
});
}
const fileInput = this.shadowRoot?.querySelector("#file-input");
if (fileInput) {
fileInput.addEventListener("change", (event) => {
const file = event.target.files?.[0];
if (file) {
this.sendFile(file);
}
});
}
}
connectedCallback() {
this.messagesMock = messageStore.getMessages();
if (this.messagesMock.length === 0) {
messageStore.setMessages(messagesMock);
this.messagesMock = messageStore.getMessages();
}
this.updateCurrentUserDisplay();
this.initializeEventListeners();
this.loadGroupList();
}
}
customElements.define("signature-element", SignatureElement);
const signatureCss = "/* Styles de base */\n:root {\n --primary-color: #3A506B;\n /* Bleu métallique */\n --secondary-color: #B0BEC5;\n /* Gris acier */\n --accent-color: #D68C45;\n /* Cuivre */\n}\n\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n padding: 0;\n display: flex;\n height: 100vh;\n background-color: #e9edf1;\n flex-direction: column;\n}\n\n\n\n/* 4NK NAVBAR */\n\n.brand-logo {\n text-align: center;\n font-size: 1.5em;\n font-weight: bold;\n}\n\n.nav-wrapper {\n position: fixed;\n background: radial-gradient(circle, white, var(--primary-color));\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #37474F;\n height: 9vh;\n width: 100vw;\n left: 0;\n top: 0;\n box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);\n}\n\n/* Icônes de la barre de navigation */\n.nav-right-icons {\n display: flex;\n}\n\n.notification-bell,\n.burger-menu {\n height: 20px;\n width: 20px;\n margin-right: 1rem;\n cursor: pointer;\n}\n\n.notification-container {\n position: relative;\n /* Conserve la position pour le notification-board */\n display: inline-flex;\n align-items: center;\n}\n\n.notification-board {\n position: absolute;\n /* Position absolue pour le placer par rapport au container */\n top: 40px;\n right: 0;\n background-color: white;\n border: 1px solid #ccc;\n padding: 10px;\n width: 200px;\n max-height: 300px;\n overflow-y: auto;\n /* Scroll si les notifications dépassent la taille */\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n z-index: 10;\n /* Définit la priorité d'affichage au-dessus des autres éléments */\n display: none;\n /* Par défaut, la notification est masquée */\n}\n\n.notification-item{\n cursor: pointer;\n}\n\n.notification-badge {\n position: absolute;\n top: -18px;\n right: 35px;\n background-color: red;\n color: white;\n border-radius: 50%;\n padding: 4px 8px;\n font-size: 12px;\n display: none;\n /* S'affiche seulement lorsqu'il y a des notifications */\n z-index: 10;\n}\n\n/* Par défaut, le menu est masqué */\n#menu {\n display: none;\n /* Menu caché par défaut */\n transition: display 0.3s ease-in-out;\n}\n\n.burger-menu {\n cursor: pointer;\n}\n\n/* Icône burger */\n#burger-icon {\n cursor: pointer;\n}\n\n.menu-content {\n display: none;\n position: absolute;\n top: 3.4rem;\n right: 1rem;\n background-color: white;\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n border-radius: 5px;\n overflow: hidden;\n}\n\n.menu-content a {\n display: block;\n padding: 10px 20px;\n text-decoration: none;\n color: #333;\n border-bottom: 1px solid #e0e0e0;\n\n &:hover {\n background-color: rgba(26, 28, 24, .08);\n }\n}\n\n.menu-content a:last-child {\n border-bottom: none;\n}\n\n/* Ajustement pour la barre de navigation fixe */\n.container {\n display: flex;\n flex: 1;\n height: 90vh;\n margin-top: 9vh;\n margin-left: -1%;\n text-align: left;\n width: 209vh;\n}\n\n\n/* Liste des groupes */\n\n.group-list {\n width: 25%;\n background-color: #1f2c3d;\n color: white;\n padding: 20px;\n box-sizing: border-box;\n overflow-y: auto;\n border-right: 2px solid #2c3e50;\n flex-shrink: 0;\n padding-right: 10px;\n height: 91vh;\n}\n\n.group-list ul {\n cursor: pointer;\n list-style: none;\n padding: 0;\n padding-right: 10px;\n margin-left: 20px;\n}\n\n.group-list li {\n margin-bottom: 10px;\n padding: 15px;\n border-radius: 8px;\n background-color: #273646;\n cursor: pointer;\n transition: background-color 0.3s, box-shadow 0.3s;\n}\n\n.group-list li:hover {\n background-color: #34495e;\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);\n}\n\n\n/* Zone de chat */\n\n.chat-area {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n background-color: #ffffff;\n border-radius: 10px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);\n margin: 0px;\n margin-top: 20px;\n margin-left: 1%;\n margin-bottom: -7px;\n}\n\n/* En-tête du chat */\n.chat-header {\n background-color: #34495e;\n color: white;\n padding: 15px;\n font-size: 20px;\n font-weight: bold;\n border-radius: 10px 10px 0 0;\n text-align: center;\n}\n\n/* Messages */\n.messages {\n flex: 1;\n padding: 20px;\n overflow-y: auto;\n background-color: #f1f1f1;\n border-top: 1px solid #ddd;\n}\n\n.message-container {\n max-width: 100%;\n border-radius: 5px;\n overflow-wrap: break-word;\n word-wrap: break-word;\n background-color: #f1f1f1;\n display: flex; \n flex-direction: column; \n}\n\n.message-container .message {\n align-self: flex-start; \n}\n\n.message-container .message.user {\n align-self: flex-end; \n margin-left: auto; \n color: white;\n}\n\n.message {\n padding: 12px 18px;\n background-color: #e1e1e1;\n border-radius: 15px;\n max-width: 70%;\n font-size: 16px;\n line-height: 1.4;\n margin-bottom: 0%;\n white-space: pre-wrap;\n word-wrap: break-word;\n position: relative;\n display: inline-block;\n}\n\n/* Messages de l'utilisateur */\n.message.user {\n background-color: #3498db;\n color: white;\n align-self: flex-end;\n text-align: right;\n}\n\n/* Amélioration de l'esthétique des messages */\n/* .message.user:before {\n content: '';\n position: absolute;\n top: 10px;\n right: -10px;\n border: 10px solid transparent;\n border-left-color: #3498db;\n} */\n\n/* Zone de saisie */\n.input-area {\n padding: 10px;\n background-color: #bdc3c7;\n display: flex;\n align-items: center;\n /* Alignement vertical */\n}\n\n.input-area input[type=\"text\"] {\n flex: 1;\n /* Prend l'espace restant */\n padding: 10px;\n border: 1px solid #ccc;\n border-radius: 5px;\n}\n\n.input-area .attachment-icon {\n margin: 0 10px;\n cursor: pointer;\n display: flex;\n align-items: center;\n}\n\n.input-area button {\n padding: 10px;\n margin-left: 10px;\n background-color: #2980b9;\n color: white;\n border: none;\n border-radius: 5px;\n cursor: pointer;\n}\n\n.input-area button:hover {\n background-color: #1f608d;\n}\n\n#message-input {\n width: 100%;\n height: 50px;\n resize: none;\n padding: 10px;\n box-sizing: border-box;\n overflow: auto;\n max-width: 100%;\n border-radius: 10px;\n}\n\n/* Responsive */\n@media screen and (max-width: 768px) {\n .group-list {\n display: none;\n /* Masquer la liste des groupes sur les petits écrans */\n }\n\n .chat-area {\n margin: 0;\n }\n}\n\n#process-details {\n flex: 1;\n background: white;\n border-radius: 8px;\n margin: 10px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n display: none;\n overflow: hidden;\n}\n\n.process-details-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px;\n background: #f8f9fa;\n border-bottom: 1px solid #eee;\n border-radius: 8px 8px 0 0;\n}\n\n.process-details-header h2 {\n margin: 0;\n color: #333;\n}\n\n.close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n}\n\n.close-btn:hover {\n color: #333;\n}\n\n.process-details-content {\n padding: 20px;\n overflow-y: auto;\n height: calc(100% - 70px);\n}\n\n.details-section {\n margin-bottom: 30px;\n}\n\n.details-section h3 {\n color: #444;\n margin-bottom: 15px;\n padding-bottom: 8px;\n border-bottom: 2px solid #f0f0f0;\n}\n\n.documents-list {\n list-style: none;\n padding: 0;\n}\n\n.documents-list li {\n padding: 8px 0;\n border-bottom: 1px solid #eee;\n}\n\n.roles-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 20px;\n}\n\n.role-block {\n background: #f8f9fa;\n border-radius: 8px;\n padding: 15px;\n border: 1px solid #eee;\n}\n\n.role-block h4 {\n color: #555;\n margin: 0 0 10px 0;\n padding-bottom: 8px;\n border-bottom: 1px solid #eee;\n}\n\n.members-list {\n list-style: none;\n padding: 0;\n}\n\n.members-list li {\n padding: 8px 12px;\n margin: 4px 0;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.2s;\n}\n\n.members-list li:hover {\n background-color: #e9ecef;\n}\n\n.group-list-item {\n padding: 8px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n\n.group-item-container {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n}\n\n.process-name {\n flex-grow: 1;\n cursor: pointer;\n}\n\n.settings-icon {\n cursor: pointer;\n padding: 5px 8px;\n margin-left: 10px;\n border-radius: 4px;\n}\n\n.settings-icon:hover {\n background-color: rgba(255, 255, 255, 0.1);\n}\n\n.process-details {\n position: fixed;\n top: 9vh;\n right: 0;\n bottom: 0;\n left: 23.5%;\n background-color: white;\n box-sizing: border-box;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n border-radius: 8px;\n margin: 10px;\n z-index: 990;\n}\n\n.process-details-content {\n height: calc(100% - 60px); /* Ajusté pour tenir compte du header */\n overflow-y: auto;\n padding: 20px;\n}\n\n.documents-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 20px;\n margin-top: 15px;\n}\n\n.document-card {\n background: white;\n border-radius: 8px;\n padding: 15px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n border: 1px solid #eee;\n}\n\n.document-card.public {\n border-left: 4px solid #4CAF50;\n}\n\n.document-card.private {\n border-left: 4px solid #FFC107;\n}\n\n.document-card.confidential {\n border-left: 4px solid #F44336;\n}\n\n.document-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 10px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n}\n\n.document-header h4 {\n margin: 0;\n color: #333;\n}\n\n.document-visibility {\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: bold;\n}\n\n.public .document-visibility {\n background-color: #E8F5E9;\n color: #2E7D32;\n}\n\n.private .document-visibility {\n background-color: #FFF3E0;\n color: #F57C00;\n}\n\n.confidential .document-visibility {\n background-color: #FFEBEE;\n color: #C62828;\n}\n\n.document-info {\n margin: 10px 0;\n font-size: 14px;\n color: #666;\n}\n\n.document-info p {\n margin: 5px 0;\n}\n\n.signatures-list {\n margin-top: 10px;\n}\n\n.signature-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px;\n border-radius: 4px;\n margin: 5px 0;\n background-color: #f8f9fa;\n}\n\n.signature-item.signed {\n background-color: #E8F5E9;\n}\n\n.signature-item.pending {\n background-color: #FFF3E0;\n}\n\n.signer-name {\n font-weight: 500;\n}\n\n.signature-status {\n font-size: 12px;\n}\n\n.signed .signature-status {\n color: #2E7D32;\n}\n\n.pending .signature-status {\n color: #F57C00;\n}\n\n.user-selector {\n position: relative;\n margin-right: 20px;\n}\n\n#userSwitchBtn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 8px 12px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n color: var(--primary-color);\n}\n\n#userSwitchBtn:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.current-user-info {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.user-avatar {\n background-color: var(--primary-color);\n color: white;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: bold;\n}\n\n.user-list {\n position: absolute;\n top: 100%;\n right: 0;\n background: white;\n border-radius: 4px;\n box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n display: none;\n z-index: 1000;\n max-height: 400px;\n overflow-y: auto;\n width: 250px;\n}\n\n.user-list.show {\n display: block;\n}\n\n.user-list-item {\n padding: 8px 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n}\n\n.user-list-item:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.user-list-item .user-avatar {\n width: 24px;\n height: 24px;\n font-size: 12px;\n}\n\n.user-list-item .user-email {\n font-size: 12px;\n color: #666;\n display: block;\n}\n\n.document-card {\n margin-bottom: 20px;\n padding: 10px;\n border: 1px solid #ccc;\n border-radius: 5px;\n}\n\n.progress-bar {\n background-color: #f3f3f3;\n border-radius: 5px;\n height: 10px;\n width: 100%;\n margin-top: 5px;\n}\n\n.progress {\n background-color: #4caf50; /* Couleur de la barre de progression */\n height: 100%;\n border-radius: 5px;\n}\n\n.new-request-btn {\n background-color: #4caf50; \n color: white; \n border: none;\n border-radius: 5px; \n padding: 10px 15px; \n cursor: pointer; \n margin-left: 10px;\n}\n\n.new-request-btn:hover {\n background-color: #45a049; \n}\n\n.header-buttons {\n display: flex;\n align-items: center; \n gap: 10px; \n}\n\n.close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n}\n\n.close-btn:hover {\n color: #333;\n}\n\n.new-request-view {\n padding: 20px;\n background-color: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n margin: 20px;\n}\n\n.upload-area {\n border: 2px dashed #ccc;\n padding: 20px;\n text-align: center;\n margin: 20px 0;\n}\n\n.upload-icon {\n font-size: 50px;\n margin: 10px 0;\n}\n\n/* New Request View */\n.new-request-view {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: white;\n z-index: 1000; \n padding: 20px;\n box-sizing: border-box;\n overflow-y: auto;\n}\n\n.upload-area {\n border: 2px dashed #ccc;\n padding: 40px;\n text-align: center;\n margin: 20px auto;\n max-width: 600px;\n background-color: #f9f9f9;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.upload-icon {\n font-size: 50px;\n margin: 20px 0;\n}\n\n.new-request-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: 60px;\n padding: 0 20px;\n}\n\n.upload-area {\n border: 2px dashed #ccc;\n padding: 40px;\n text-align: center;\n margin: 20px auto;\n max-width: 600px;\n background-color: #f9f9f9;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.details-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #f8f9fa;\n padding: 5px 20px;\n border-radius:20px;\n}\n\n.header-buttons {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-direction: row; \n}\n\n.close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n padding: 5px;\n}\n\n.close-btn:hover {\n color: #333;\n}\n\n.new-request-btn {\n background-color: #4caf50;\n color: white;\n border: none;\n border-radius: 5px;\n padding: 8px 16px;\n cursor: pointer;\n /* margin-left: 10px; <- Supprimez cette ligne si elle existe */\n}\n\n.new-request-btn:hover {\n background-color: #45a049;\n}\n\n/* Ajoutez ces styles à votre fichier CSS existant */\n.document-card.vierge {\n background-color: #fff3cd;\n border: 2px solid #ffeeba;\n}\n\n.document-card.vierge .document-header {\n background-color: #fff3cd;\n}\n\n.document-card.vierge h4 {\n color: #856404;\n}\n\n.document-card.vierge .document-info {\n color: #856404;\n}\n\n/* Style pour l'emoji d'avertissement */\n.document-card.vierge h4::before {\n margin-right: 8px;\n}\n\n.vierge-documents-container {\n padding: 20px;\n overflow-y: auto;\n max-height: calc(100vh - 150px);\n}\n\n.document-form {\n padding: 15px;\n background-color: #fff;\n border-radius: 0 0 5px 5px;\n display: flex;\n gap: 20px;\n}\n\n.form-left {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 15px;\n}\n\n.form-right {\n display: flex;\n flex-direction: column;\n align-items: flex-end; /* Aligner le bouton à droite */\n}\n\n.form-group {\n flex: 2;\n display: flex;\n flex-direction: column;\n margin-bottom: 15px;\n}\n\n.form-group-members {\n flex: 2;\n display: flex;\n flex-direction: column;\n margin-bottom: 15px;\n font-weight: bold;\n}\n\n.submit-btn {\n background-color: #4caf50; \n color: white; \n border: none;\n border-radius: 5px; \n padding: 10px 15px; \n cursor: pointer; \n margin-left: 47%;\n margin-top: 20px;\n}\n\n.submit-btn:hover {\n background-color: #45a049;\n}\n\n.upload-format {\n font-size: 12px;\n color: #666;\n margin: 5px 0;\n}\n\n.browse-btn {\n background-color: #4caf50;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 10px;\n}\n\n.browse-btn:hover {\n background-color: #45a049;\n}\n\n.document-selector {\n padding: 20px;\n margin-bottom: 20px;\n}\n\n.document-selector label {\n display: block;\n margin-bottom: 10px;\n font-weight: bold;\n color: #666;\n}\n\n.document-selector select {\n width: 100%;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 16px;\n background-color: white;\n}\n\n#selected-document-form {\n padding: 0 20px;\n overflow: auto;\n height: 65%;\n}\n\n/* Style pour l'option avec l'emoji */\n.document-selector select option {\n padding: 10px;\n font-size: 14px;\n}\n\n.members-selection {\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #ddd;\n border-radius: 4px;\n padding: 10px;\n background-color: white;\n}\n\n.member-checkbox {\n display: flex;\n align-items: center;\n margin-bottom: 8px;\n padding: 5px;\n}\n\n.member-checkbox:hover {\n background-color: #f5f5f5;\n}\n\n.member-checkbox input[type=\"checkbox\"] {\n margin-right: 10px;\n}\n\n.member-checkbox label {\n cursor: pointer;\n flex: 1;\n}\n\n/* Style pour le conteneur des membres */\n.members-selection-container {\n display: flex;\n flex-direction: column;\n align-items: center; /* Centrer la liste des membres */\n width: 100%;\n}\n\n#members-list {\n width: 60%;\n height: 100%;\n margin-bottom: -40px;\n margin-top: 10px;\n}\n\n/* Style pour le label des membres */\n.members-selection-container label {\n font-weight: bold; /* Mettre le texte en gras */\n margin-bottom: 10px; /* Espacement en bas */\n display: block; /* Affichage en bloc */\n}\n\n/* Style pour les cases à cocher des membres */\n.member-checkbox {\n display: flex;\n align-items: center;\n margin-bottom: 8px;\n padding: 5px;\n border-radius: 4px; /* Coins arrondis */\n transition: background-color 0.2s; /* Transition pour l'effet de survol */\n}\n\n.member-checkbox:hover {\n background-color: #e9ecef; /* Couleur de fond au survol */\n}\n\n/* Style pour les labels */\n.form-group label {\n font-weight: bold; \n margin-bottom: 5px; \n}\n\n/* Style pour les champs de saisie */\n.form-group input,\n.form-group select {\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 16px;\n width: 80%;\n}\n\n.add-members-btn {\n background-color: #4caf50; \n color: white; \n border: none; \n border-radius: 5px; \n padding: 10px 15px; \n cursor: pointer; \n margin-top: 10px; /* Espacement au-dessus du bouton */\n}\n\n.add-members-btn:hover {\n background-color: #45a049; \n}\n\n/* Styles pour l'overlay et la modale */\n.modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n z-index: 999;\n}\n\n.modal {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background-color: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n z-index: 1000;\n width: 25%;\n max-width: 500px;\n}\n\n.modal-content {\n max-height: 70vh;\n overflow-y: auto;\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n margin-top: 20px;\n padding-top: 10px;\n border-top: 1px solid #eee;\n}\n\n.confirm-btn {\n background-color: #4caf50;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.confirm-btn:hover {\n background-color: #45a049;\n}\n\n.selected-member {\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #f5f5f5;\n padding: 8px 12px;\n margin: 4px 0;\n border-radius: 4px;\n}\n\n.cancel-btn {\n background-color: #df2020; \n color: white; \n border: none;\n padding: 8px 16px; \n border-radius: 4px; \n cursor: pointer;\n}\n\n.cancel-btn:hover {\n background-color: #c62828; /\n}\n\n.remove-member-btn {\n background-color: #dc3545;\n color: white;\n border: none;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n font-size: 16px;\n padding: 0;\n margin-left: 8px;\n}\n\n.remove-member-btn:hover {\n background-color: #c82333;\n}\n\n#description {\n height: 100px;\n width: 100%;\n border-radius: 20px;\n padding: 10px 0;\n padding-left: 10px;\n resize: none;\n\n}\n\n.role-item-container {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n padding: 5px 0;\n}\n\n.role-item-container span:last-child {\n margin-left: 10px;\n}\n\n.document-card .new-request-btn {\n margin-top: 60%;\n margin-left: 65%;\n padding: 6px 12px;\n background-color: #4CAF50;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 0.9em;\n}\n\n.document-card .new-request-btn:hover {\n background-color: #45a049;\n}\n\n/* Styles pour la modale de nouveau document */\n.modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000;\n}\n\n\n.modal-content-document {\n background: white;\n padding: 20px;\n border-radius: 8px;\n width: 97%;\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n}\n\n.modal-header h2 {\n margin: 0;\n color: #333;\n}\n\n.modal-body {\n margin-bottom: 20px;\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding-top: 20px;\n border-top: 1px solid #eee;\n}\n\n.modal-document {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000;\n}\n.form-group {\n margin-bottom: 15px;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 5px;\n font-weight: bold;\n color: #555;\n}\n\n.form-control {\n width: 100%;\n padding: 8px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n}\n\n.form-row {\n display: flex;\n gap: 15px;\n margin-bottom: 15px;\n}\n\n.form-group.half {\n flex: 1;\n margin-bottom: 0; /* Annule la marge du form-group standard */\n}\n\n.selected-signatories {\n margin: 10px 0;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n min-height: 50px;\n}\n\n.signatory-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 5px;\n margin: 5px 0;\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n.remove-btn {\n background: none;\n border: none;\n color: #dc3545;\n cursor: pointer;\n font-size: 18px;\n padding: 0 5px;\n}\n\n.remove-btn:hover {\n color: #bd2130;\n}\n\n.btn-primary {\n background: #4CAF50;\n color: white;\n padding: 10px 20px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.btn-primary:hover {\n background: #45a049;\n}\n\n.btn-secondary {\n background: #6c757d;\n color: white;\n padding: 8px 15px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.btn-secondary:hover {\n background: #5a6268;\n}\n\n.signatories-list {\n max-height: 300px;\n overflow-y: auto;\n}\n\n.signatory-option {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px;\n border-bottom: 1px solid #eee;\n}\n\n.role-select {\n padding: 4px;\n border: 1px solid #ddd;\n border-radius: 4px;\n margin-left: auto;\n}\n\n.role-section {\n margin-bottom: 20px;\n padding: 10px;\n background: #f8f9fa;\n border-radius: 4px;\n}\n\n.role-section h4 {\n margin: 0 0 10px 0;\n color: #495057;\n}\n\n.members-selection {\n max-height: 300px;\n overflow-y: auto;\n padding: 10px;\n border: 1px solid #dee2e6;\n border-radius: 4px;\n}\n\ninput[type=\"file\"] {\n padding: 10px;\n border: 1px dashed #ccc;\n border-radius: 4px;\n width: 100%;\n margin-top: 5px;\n}\n\n.file-upload-container {\n border: 2px dashed #ccc;\n padding: 20px;\n text-align: center;\n margin: 10px 0;\n border-radius: 5px;\n cursor: pointer;\n}\n\n.file-upload-container:hover {\n background-color: #f5f5f5;\n border-color: #666;\n}\n\n.file-upload-container.dragover {\n background-color: #f0f0f0;\n border-color: #4CAF50;\n}\n\n.file-list {\n margin-top: 10px;\n}\n\n.file-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px;\n margin: 5px 0;\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n.file-info {\n display: flex;\n gap: 10px;\n align-items: center;\n}\n\n.remove-file {\n background: none;\n border: none;\n color: #ff4444;\n cursor: pointer;\n font-size: 18px;\n}\n\n#fileInput {\n display: none;\n}\n\n.required-signatories {\n margin: 10px 0;\n}\n\n.signatory-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px;\n margin: 5px 0;\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n.signatory-item.locked {\n background: #e8e8e8;\n cursor: not-allowed;\n}\n\n.member-name {\n font-weight: 500;\n}\n\n.role-info {\n color: #666;\n font-size: 0.9em;\n}\n\n.lock-icon {\n margin-left: auto;\n opacity: 0.6;\n}\n\n/* Style pour la popup d'alerte */\n.alert-popup {\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background-color: #f44336;\n color: white;\n padding: 15px 25px;\n border-radius: 4px;\n box-shadow: 0 2px 5px rgba(0,0,0,0.2);\n z-index: 1000;\n display: none;\n animation: slideDown 0.3s ease-out;\n}\n\n.sign-button {\n background-color: #4CAF50;\n color: white;\n padding: 8px 16px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 10px;\n}\n\n.sign-button:hover {\n background-color: #45a049;\n}\n\n.sign-button:disabled {\n background-color: #cccccc;\n cursor: not-allowed;\n}\n\n.modal-document {\n background: white;\n border-radius: 8px;\n max-width: 800px;\n width: 90%;\n max-height: 95vh;\n overflow-y: auto;\n position: relative;\n}\n\n.document-details {\n padding: 20px;\n}\n\n.info-section {\n margin: 20px 0;\n background: #f8f9fa;\n padding: 15px;\n border-radius: 6px;\n}\n\n.info-row {\n display: flex;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.label {\n font-weight: bold;\n color: #666;\n}\n\n.description-section {\n margin: 20px 0;\n}\n\n.signatures-section {\n margin: 20px 0;\n}\n\n.signature-item {\n display: flex;\n justify-content: space-between;\n padding: 8px;\n border-bottom: 1px solid #eee;\n}\n\n.signature-item.signed {\n background-color: #e8f5e9;\n}\n\n.signature-item.pending {\n background-color: #fff3e0;\n}\n\n.files-section {\n margin: 20px 0;\n}\n\n.file-item {\n display: flex;\n align-items: center;\n padding: 8px;\n background: #f8f9fa;\n margin-bottom: 5px;\n border-radius: 4px;\n}\n\n.file-icon {\n margin-right: 10px;\n}\n\n.download-link {\n margin-left: auto;\n}\n\n.confirmation-section {\n margin-top: 30px;\n text-align: center;\n}\n\n.warning-text {\n color: #f44336;\n margin-bottom: 15px;\n}\n\n.sign-confirm-btn {\n background-color: #4CAF50;\n color: white;\n padding: 10px 20px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.sign-confirm-btn:hover {\n background-color: #45a049;\n}\n\n.signature-slider-container {\n margin: 20px 20%;\n padding: 10px;\n}\n\n.slider-track {\n position: relative;\n background: #e0e0e0;\n height: 40px;\n border-radius: 20px;\n display: flex;\n align-items: center;\n overflow: hidden;\n}\n\n.slider-handle {\n position: absolute;\n left: 0;\n width: 40px;\n height: 40px;\n background: #4CAF50;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: none;\n z-index: 1;\n}\n\n.slider-arrow {\n color: white;\n font-size: 20px;\n}\n\n.slider-text {\n width: 100%;\n text-align: center;\n color: #666;\n user-select: none;\n}";
class SignatureComponent extends HTMLElement {
_callback;
signatureElement = null;
constructor() {
super();
console.log("INIT");
this.attachShadow({ mode: "open" });
this.signatureElement = this.shadowRoot?.querySelector("signature-element") || null;
}
connectedCallback() {
console.log("CALLBACKs");
this.render();
this.fetchData();
if (!customElements.get("signature-element")) {
customElements.define("signature-element", SignatureElement);
}
}
async fetchData() {
{
const service = await Services.getInstance();
await service.getProcesses();
}
}
set callback(fn) {
if (typeof fn === "function") {
this._callback = fn;
} else {
console.error("Callback is not a function");
}
}
get callback() {
return this._callback;
}
render() {
if (this.shadowRoot) {
const signatureElement = document.createElement("signature-element");
this.shadowRoot.innerHTML = `<style>${signatureCss}</style>`;
this.shadowRoot.appendChild(signatureElement);
}
}
}
customElements.define("signature-component", SignatureComponent);
export { SignatureComponent };