2105 lines
143 KiB
JavaScript
2105 lines
143 KiB
JavaScript
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 };
|