diff --git a/package-lock.json b/package-lock.json
index 63e903c..2dd019e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,10 +14,9 @@
"@vitejs/plugin-vue": "^5.0.5",
"html5-qrcode": "^2.3.8",
"qrcode": "^1.5.3",
- "vite": "^5.4.9",
+ "sweetalert2": "^11.14.5",
"vite-plugin-copy": "^0.1.6",
"vite-plugin-html": "^3.2.2",
- "vite-plugin-terminal": "^1.2.0",
"vite-plugin-wasm": "^3.3.0"
},
"devDependencies": {
@@ -26,6 +25,7 @@
"prettier": "^3.3.3",
"ts-loader": "^9.5.1",
"typescript": "^5.3.3",
+ "vite": "^5.4.11",
"vite-plugin-static-copy": "^1.0.6",
"webpack": "^5.90.3",
"webpack-cli": "^5.1.4",
@@ -904,56 +904,6 @@
"node": ">=14"
}
},
- "node_modules/@polka/url": {
- "version": "1.0.0-next.28",
- "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz",
- "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
- "license": "MIT"
- },
- "node_modules/@rollup/plugin-strip": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-strip/-/plugin-strip-3.0.4.tgz",
- "integrity": "sha512-LDRV49ZaavxUo2YoKKMQjCxzCxugu1rCPQa0lDYBOWLj6vtzBMr8DcoJjsmg+s450RbKbe3qI9ZLaSO+O1oNbg==",
- "license": "MIT",
- "dependencies": {
- "@rollup/pluginutils": "^5.0.1",
- "estree-walker": "^2.0.2",
- "magic-string": "^0.30.3"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
- "node_modules/@rollup/plugin-strip/node_modules/@rollup/pluginutils": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz",
- "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==",
- "license": "MIT",
- "dependencies": {
- "@types/estree": "^1.0.0",
- "estree-walker": "^2.0.2",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
"node_modules/@rollup/pluginutils": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
@@ -4188,12 +4138,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/kolorist": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
- "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
- "license": "MIT"
- },
"node_modules/launch-editor": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.0.tgz",
@@ -4250,6 +4194,7 @@
"version": "0.30.10",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
"integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
+ "peer": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
}
@@ -4391,15 +4336,6 @@
"node": ">=16 || 14 >=14.17"
}
},
- "node_modules/mrmime": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
- "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -5512,20 +5448,6 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
},
- "node_modules/sirv": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
- "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
- "license": "MIT",
- "dependencies": {
- "@polka/url": "^1.0.0-next.24",
- "mrmime": "^2.0.0",
- "totalist": "^3.0.0"
- },
- "engines": {
- "node": ">= 10"
- }
- },
"node_modules/slash": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
@@ -5744,6 +5666,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/sweetalert2": {
+ "version": "11.14.5",
+ "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.14.5.tgz",
+ "integrity": "sha512-8MWk5uc/r6bWhiJWkUXyEuApfXAhSCZT8FFX7pZXL7YwaPxq+9Ynhi2dUzWkOFn9jvLjKj22CXuccZ+IHcnjvQ==",
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/limonte"
+ }
+ },
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -5904,15 +5835,6 @@
"node": ">=0.6"
}
},
- "node_modules/totalist": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
- "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/tree-dump": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz",
@@ -6071,12 +5993,6 @@
"node": ">=14.17"
}
},
- "node_modules/ufo": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz",
- "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==",
- "license": "MIT"
- },
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
@@ -6189,10 +6105,9 @@
}
},
"node_modules/vite": {
- "version": "5.4.9",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
- "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
- "license": "MIT",
+ "version": "5.4.11",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
+ "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -6312,28 +6227,6 @@
"node": ">=14.14"
}
},
- "node_modules/vite-plugin-terminal": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/vite-plugin-terminal/-/vite-plugin-terminal-1.2.0.tgz",
- "integrity": "sha512-IIw1V+IySth8xlrGmH4U7YmfTp681vTzYpa7b8A3KNCJ2oW1BGPPwW8tSz6BQTvSgbRmrP/9NsBLsfXkN4e8sA==",
- "license": "MIT",
- "dependencies": {
- "@rollup/plugin-strip": "^3.0.2",
- "debug": "^4.3.4",
- "kolorist": "^1.7.0",
- "sirv": "^2.0.2",
- "ufo": "^1.1.1"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/patak-dev"
- },
- "peerDependencies": {
- "vite": "^2.0.0||^3.0.0||^4.0.0||^5.0.0"
- }
- },
"node_modules/vite-plugin-wasm": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.3.0.tgz",
diff --git a/package.json b/package.json
index aeb5729..77d2e63 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"prettier": "^3.3.3",
"ts-loader": "^9.5.1",
"typescript": "^5.3.3",
+ "vite": "^5.4.11",
"vite-plugin-static-copy": "^1.0.6",
"webpack": "^5.90.3",
"webpack-cli": "^5.1.4",
@@ -31,7 +32,7 @@
"@vitejs/plugin-vue": "^5.0.5",
"html5-qrcode": "^2.3.8",
"qrcode": "^1.5.3",
- "vite": "^5.4.9",
+ "sweetalert2": "^11.14.5",
"vite-plugin-copy": "^0.1.6",
"vite-plugin-html": "^3.2.2",
"vite-plugin-wasm": "^3.3.0"
diff --git a/public/style/account.css b/public/style/account.css
new file mode 100644
index 0000000..b0e35bd
--- /dev/null
+++ b/public/style/account.css
@@ -0,0 +1,1319 @@
+/* Styles de base */
+:root {
+ --primary-color: #3A506B;
+ /* Bleu métallique */
+ --secondary-color: #B0BEC5;
+ /* Gris acier */
+ --accent-color: #D68C45;
+ /* Cuivre */
+}
+
+body {
+ font-family: Arial, sans-serif;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ height: 100vh;
+ background-color: #e9edf1;
+ flex-direction: column;
+}
+
+/*-------------------------------------- Avatar--------------------------------------*/
+
+.avatar-section {
+ position: relative;
+ height: 60px;
+ width: 260px;
+ margin-left: 10px;
+ overflow: hidden;
+ border-radius: 10px;
+}
+
+.user-info {
+ display: flex;
+ flex-direction: column;
+ color: white;
+}
+
+.user-name, .user-lastname {
+ font-size: 0.9rem;
+ font-weight: 700;
+}
+
+
+.user-name:hover, .user-lastname:hover {
+ color: var(--accent-color);
+ cursor: pointer;
+}
+
+.avatar-container {
+ width: 45px;
+ height: 45px;
+ flex-shrink: 0;
+}
+
+.avatar-container {
+ width: 80px; /* Taille réduite */
+ height: 80px;
+ margin: 0 auto;
+}
+
+.avatar {
+ height: 100%;
+ border-radius: 50%;
+ object-fit: cover;
+ border: 2px solid white;
+}
+
+.avatar img {
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+}
+
+
+/*-------------------------------------- BANNER--------------------------------------*/
+
+/* Styles pour la bannière avec image */
+.banner-image-container {
+ position: relative;
+ width: 100%;
+ height: 200px;
+ overflow: hidden;
+ border-radius: 10px;
+ margin-bottom: 15px;
+}
+
+.banner-image {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ z-index: 1;
+}
+
+.banner-content {
+ position: relative;
+ z-index: 2;
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ height: 100%;
+ padding: 0 15px;
+ background: rgba(0, 0, 0, 0.3);
+ overflow: visible;
+}
+
+.banner-content .avatar-container {
+ width: 45px;
+ height: 45px;
+ overflow: hidden;
+ border-radius: 50%;
+ border: 2px solid white;
+ transition: transform 0.3s ease;
+}
+
+.banner-content .avatar {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border: none;
+}
+
+.banner-content .avatar-container:hover {
+ transform: scale(1.15);
+ cursor: pointer;
+}
+
+/* Style pour le bouton de changement de bannière */
+.banner-upload-label {
+ display: block;
+ width: auto;
+ padding: 12px 20px;
+ background-color: var(--accent-color);
+ color: white;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ text-align: center;
+ font-size: 16px;
+ margin: 20px auto;
+ max-width: 250px;
+}
+
+.banner-upload-label:hover {
+ background-color: #b06935;
+}
+
+.banner-controls {
+ margin-top: 15px;
+ display: flex;
+ justify-content: center;
+ width: 100%;
+}
+
+.banner-preview {
+ margin: 10px 0;
+}
+
+.banner-preview h3 {
+ margin: 0 0 10px 0;
+ font-size: 18px;
+}
+
+.banner-image-container {
+ height: 150px;
+ margin-bottom: 10px;
+}
+
+
+.nav-wrapper {
+ position: fixed;
+ background: white;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 9vh;
+ width: 100vw;
+ left: 0;
+ top: 0;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ z-index: 1000;
+}
+/* Mise à jour des styles de la navbar pour inclure l'image de bannière */
+.nav-wrapper .avatar-section {
+ position: relative;
+ background: none;
+}
+
+.nav-wrapper .banner-image {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ z-index: -1;
+ filter: brightness(0.7);
+}
+
+
+
+/*-------------------------------------- Popup--------------------------------------*/
+/* Styles pour la popup */
+.popup {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+}
+
+.popup-content {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: white;
+ padding: 20px;
+ border-radius: var(--border-radius);
+ box-shadow: var(--box-shadow);
+ width: 400px;
+ max-height: 80vh;
+ overflow-y: auto;
+}
+.popup-content h2 {
+ margin: 0 0 15px 0;
+ font-size: 24px;
+}
+
+.close-popup {
+ position: absolute;
+ right: 15px;
+ top: 10px;
+ font-size: 24px;
+ cursor: pointer;
+ color: #666;
+}
+
+.close-popup:hover {
+ color: #000;
+}
+
+.popup-avatar {
+ text-align: center;
+ margin: 20px 0;
+ position: relative;
+}
+
+.avatar-upload-label {
+ position: relative;
+ display: inline-block;
+ cursor: pointer;
+ width: 0%;
+ margin-left: -20%;
+}
+
+.avatar-overlay {
+ position: absolute;
+ top: 0;
+ left: 50px;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ border-radius: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ opacity: 0;
+ transition: opacity 0.3s;
+}
+
+.avatar-overlay span {
+ color: white;
+ font-size: 14px;
+ text-align: center;
+}
+
+.avatar-upload-label:hover .avatar-overlay {
+ opacity: 1;
+}
+
+.popup-avatar img {
+ width: 100px;
+ height: 100px;
+ border-radius: 50%;
+ border: 3px solid var(--accent-color);
+ object-fit: cover;
+}
+
+.popup-info {
+ margin: 15px 0;
+}
+
+.info-row {
+ margin: 8px 0;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.popup-info strong {
+ min-width: 100px; /* Largeur fixe pour l'alignement */
+}
+
+/* Editable Name and Lastname */
+.editable {
+ cursor: pointer;
+ display: inline-block;
+ min-width: 100px;
+ padding: 2px 5px;
+ transition: background-color 0.3s;
+}
+
+.editable:hover {
+ background-color: #f0f0f0;
+}
+
+.editable.editing {
+ background-color: white;
+ border: 1px solid var(--accent-color);
+ outline: none;
+}
+
+.edit-input {
+ border: 1px solid var(--accent-color);
+ border-radius: 3px;
+ padding: 2px 5px;
+ font-size: inherit;
+ font-family: inherit;
+ outline: none;
+ width: 100%;
+ min-width: 100px;
+ margin: 0;
+ box-sizing: border-box;
+}
+
+/* Boutons */
+
+
+.popup-button-container {
+ display: flex
+;
+ flex-direction: column;
+ margin-top: 20px;
+ gap: 15px;
+}
+
+.action-buttons-row {
+ display: flex
+;
+ justify-content: space-between;
+ gap: 15px;
+}
+.banner-upload-label,
+.export-btn,
+.delete-account-btn {
+ padding: 8px 15px;
+ margin: 10px 0;
+ font-size: 14px;
+}
+
+.delete-account-btn {
+ background-color: #dc3545;
+}
+
+
+.export-btn,
+.delete-account-btn {
+ flex: 1; /* Pour qu'ils prennent la même largeur */
+ padding: 12px 20px;
+ border: none;
+ border-radius: 8px;
+ font-size: 16px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ color: white;
+ text-align: center;
+}
+
+
+
+/* Séparateurs */
+.popup-info,
+.export-section,
+.delete-account-section {
+ padding-top: 10px;
+ margin-top: 10px;
+ border-top: 1px solid #eee;
+}
+
+.logout-btn {
+ background-color: rgb(108, 117, 125);
+ font-size: 16px;
+ cursor: pointer;
+ color: white;
+ text-align: center;
+ flex: 1 1 0%;
+ padding: 12px 20px;
+ border-width: initial;
+ border-style: none;
+ border-color: initial;
+ border-image: initial;
+ border-radius: 8px;
+ transition: background-color 0.3s;
+}
+
+/*-------------------------------------- Delete Account--------------------------------------*/
+.delete-account-section {
+ margin-top: 30px;
+ padding-top: 20px;
+ border-top: 1px solid #ddd;
+ text-align: center;
+}
+
+.delete-account-btn {
+ background-color: #dc3545;
+}
+
+.delete-account-btn:hover {
+ background-color: #c82333;
+}
+
+/* Style pour la modal de confirmation */
+.confirm-delete-modal {
+ display: none;
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: white;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ z-index: 1100;
+ text-align: center;
+}
+
+.confirm-delete-buttons {
+ margin-top: 20px;
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+}
+
+.confirm-delete-buttons button {
+ padding: 8px 20px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.confirm-btn {
+ background-color: #dc3545;
+ color: white;
+}
+
+.cancel-btn {
+ background-color: #6c757d;
+ color: white;
+}
+
+.delete-account-section {
+ border-top: 1px solid #eee;
+ padding-top: 15px;
+ margin-top: 15px;
+}
+
+
+/*-------------------------------------- Export--------------------------------------*/
+.export-section {
+ margin: 20px 0;
+ text-align: center;
+ padding: 15px 0;
+ border-top: 1px solid #ddd;
+}
+
+.export-btn {
+ background-color: var(--accent-color);
+}
+
+.export-btn:hover {
+ background-color: #b06935;
+}
+
+.export-section,
+.delete-account-section {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ margin: 15px 0;
+}
+
+.export-btn,
+.delete-account-btn {
+ width: 80%;
+ padding: 12px 20px;
+ border: none;
+ border-radius: 8px;
+ font-size: 16px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ color: white;
+ text-align: center;
+}
+
+/*-------------------------------------- NAVBAR--------------------------------------*/
+
+.brand-logo {
+ font-size: 1.5rem;
+ font-weight: bold;
+}
+
+.nav-wrapper {
+ position: fixed;
+ background: radial-gradient(circle, white, var(--primary-color));
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ color: #37474F;
+ height: 9vh;
+ width: 100vw;
+ left: 0;
+ top: 0;
+ 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);
+}
+
+/* Icônes de la barre de navigation */
+.nav-right-icons {
+ margin-right: 20px;
+}
+
+.burger-menu {
+ height: 20px;
+ width: 20px;
+ margin-right: 1rem;
+ cursor: pointer;
+}
+
+/* Par défaut, le menu est masqué */
+#menu {
+ display: none;
+ /* Menu caché par défaut */
+ transition: display 0.3s ease-in-out;
+}
+
+
+.burger-menu {
+ width: 24px;
+ height: 24px;
+ cursor: pointer;
+}
+
+.burger-menu-icon {
+ width: 100%;
+ height: 100%;
+}
+/* Icône burger */
+#burger-icon {
+ cursor: pointer;
+}
+
+/* .menu-content {
+ display: none;
+ position: absolute;
+ top: 3.4rem;
+ right: 1rem;
+ background-color: white;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 5px;
+ overflow: hidden;
+}
+
+.menu-content a {
+ display: block;
+ padding: 10px 20px;
+ text-decoration: none;
+ color: #333;
+ border-bottom: 1px solid #e0e0e0;
+
+ &:hover {
+ background-color: rgba(26, 28, 24, .08);
+ }
+}
+
+.menu-content a:last-child {
+ border-bottom: none;
+} */
+
+/* Ajustement pour la barre de navigation fixe */
+.container {
+ display: flex;
+ flex: 1;
+ height: calc(100% - 4vh);
+ margin-top: 4vh;
+ margin-left: -1%;
+ text-align: left;
+}
+
+/* Liste des information sur l'account */
+
+.parameter-list {
+ width: 24.5%;
+ background-color: #1f2c3d;
+ color: white;
+ padding: 20px;
+ box-sizing: border-box;
+ overflow-y: auto;
+ border-right: 2px solid #2c3e50;
+ flex-shrink: 0;
+}
+
+.parameter-list ul {
+ padding: 15px;
+ margin-left: 10px;
+ border-radius: 8px;
+ background-color: #273646;
+ cursor: pointer;
+ transition: background-color 0.3s, box-shadow 0.3s;
+}
+
+
+.parameter-list ul:hover {
+ background-color: #34495e;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+}
+
+
+/* Zone des info des parametre */
+
+.parameter-area {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-width: 0;
+ background-color: #ffffff;
+ border-radius: 10px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+ margin: 10px;
+ margin-top: 20px;
+ margin-left: 0%;
+}
+
+/* En-tête du parametre */
+.parameter-header {
+ background-color: #34495e;
+ color: white;
+ padding: 15px;
+ font-size: 20px;
+ font-weight: bold;
+ border-radius: 10px 10px 0 0;
+ text-align: center;
+}
+
+/* Style du tableau dans parameter-area */
+.parameter-table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 15px 0;
+ table-layout: fixed;
+}
+
+.parameter-table th, .parameter-table td {
+ border: 1px solid #ddd;
+ padding: 8px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-align: center;
+}
+
+.parameter-table th {
+ background-color: var(--secondary-color);
+ color: white;
+ font-weight: bold;
+}
+
+.parameter-table tr:nth-child(even) {
+ background-color: #f2f2f2;
+}
+
+
+
+.parameter-table tr:hover {
+ background-color: #ddd;
+}
+
+/* Conteneur pour les boutons */
+.button-container {
+ display: flex;
+ justify-content: center;
+ gap: 15px;
+ margin: 15px 0;
+}
+
+/* Boutons "Ajouter une ligne" et "Confirmer" */
+.add-row-button, .confirm-all-button, .delete-row-button {
+ background-color: var(--accent-color);
+ color: white;
+ border: none;
+ padding: 8px 15px;
+ cursor: pointer;
+ border-radius: 5px;
+ font-size: 0.9em;
+ margin-right: 5px;
+}
+
+.add-row-button:hover, .confirm-all-button:hover, .delete-row-button:hover {
+ background-color: #b06935;
+}
+
+
+.button-style {
+ background-color: var(--accent-color);
+ color: white;
+ border: none;
+ border-radius: 5px;
+ padding: 10px 20px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.button-style:hover {
+ background-color: darkorange;
+}
+
+.content-container {
+ width: 100%;
+}
+
+#pairing-content,
+#wallet-content {
+ width: 100%;
+}
+
+.editable-cell {
+ cursor: pointer;
+}
+
+.editable-cell:hover {
+ background-color: #f5f5f5;
+}
+
+.edit-input {
+ width: 100%;
+ padding: 5px;
+ box-sizing: border-box;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+/*-------------------------------------- Notification--------------------------------------*/
+.notification-cell {
+ position: relative;
+}
+
+.notification-bell {
+ position: relative;
+ display: inline-block;
+}
+
+.notification-badge {
+ position: absolute;
+ top: -8px;
+ right: -8px;
+ background-color: red;
+ color: white;
+ border-radius: 50%;
+ padding: 2px 6px;
+ font-size: 12px;
+ min-width: 15px;
+ text-align: center;
+}
+
+.fa-bell {
+ color: #666;
+ font-size: 20px;
+}
+
+
+/* Media Queries pour Mobile */
+@media screen and (max-width: 768px) {
+ /* Navbar */
+ .nav-wrapper {
+ height: 9vh;
+ padding: 0;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ /* Section avatar (gauche) */
+ .avatar-section {
+ width: 200px; /* Largeur réduite */
+ margin-left: 10px;
+ order: 0; /* Garde à gauche */
+ }
+
+ .avatar-container {
+ width: 35px;
+ height: 35px;
+ }
+
+ .user-info span {
+ font-size: 0.8rem;
+ }
+
+ /* Logo (centre) */
+ .brand-logo {
+ order: 0;
+ flex: 0 0 auto;
+ padding: 0;
+ font-size: 1.2rem;
+ }
+
+ /* Menu burger (droite) */
+ .nav-right-icons {
+ order: 0;
+ width: auto;
+ padding-right: 15px;
+ }
+
+ .burger-menu {
+ width: 24px;
+ height: 24px;
+ }
+
+ /* Ajustements pour la bannière */
+ .banner-image-container {
+ height: 100%;
+ }
+
+ .banner-content {
+ padding: 0 10px;
+ }
+
+ /* Style des boutons dans la popup */
+ .button-container {
+ display: flex;
+ gap: 10px;
+ margin: 15px 0;
+ width: 100%;
+ }
+
+ .export-btn,
+ .delete-account-btn {
+ flex: 1;
+ padding: 12px 15px;
+ border: none;
+ border-radius: 8px;
+ font-size: 14px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ color: white;
+ }
+
+ .export-btn {
+ background-color: var(--accent-color);
+ }
+
+ .delete-account-btn {
+ background-color: var(--danger-color);
+ }
+}
+
+/* Media Queries pour très petits écrans */
+@media screen and (max-width: 380px) {
+ .avatar-section {
+ width: 150px;
+ }
+
+ .user-info span {
+ font-size: 0.7rem;
+ }
+}
+
+/* Style des boutons */
+.button-container {
+ display: flex;
+ gap: 15px;
+ margin: 15px 0;
+ width: 100%;
+}
+
+.export-btn,
+.delete-account-btn {
+ flex: 1;
+ padding: 12px 20px;
+ border: none;
+ border-radius: 8px;
+ color: white;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ font-size: 14px;
+ display: block;
+}
+
+.export-btn {
+ background-color: var(--accent-color);
+}
+
+.delete-account-btn {
+ background-color: rgb(219, 17, 17);
+ display: block;
+ visibility: visible;
+}
+
+.export-btn:hover {
+ background-color: #b06935;
+}
+
+.delete-account-btn:hover {
+ background-color: #b60718;
+}
+
+@media screen and (max-width: 768px) {
+ .button-container {
+ gap: 10px;
+ }
+
+ .export-btn,
+ .delete-account-btn {
+ padding: 12px 15px;
+ font-size: 14px;
+ }
+}
+
+/* Style pour les boutons de la popup */
+.popup-buttons {
+ display: flex;
+ gap: 15px;
+ margin: 15px 0;
+ width: 100%;
+}
+
+/* Style pour les boutons d'action des tableaux */
+.button-container {
+ display: flex;
+ gap: 15px;
+ margin: 15px 0;
+ width: 100%;
+}
+
+/* Style pour le header mobile */
+.mobile-nav {
+ display: none;
+ width: 100%;
+ padding: 10px;
+ background-color: #34495e;
+ overflow-x: auto;
+ white-space: nowrap;
+}
+
+.mobile-nav ul {
+ display: flex;
+ gap: 10px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+/* Media Query pour mobile */
+@media screen and (max-width: 768px) {
+ .parameter-list {
+ display: flex;
+ width: 100%;
+ min-width: 100%;
+ height: auto;
+ overflow-x: auto;
+ background-color: rgb(31, 44, 61);
+ padding: 10px;
+ border-right: none;
+ border-bottom: 2px solid rgb(44, 62, 80);
+ }
+
+ .mobile-nav {
+ display: flex; /* Affiche la navigation mobile */
+ }
+
+ .parameter-header {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .parameter-list-ul {
+ text-align: center;
+ flex: 1 1 0%;
+ margin: 0px 5px;
+ padding: 10px;
+ white-space: nowrap;
+ margin-bottom: none;
+ }
+
+ .parameter-list-ul:hover {
+ background-color: #34495e;
+ }
+
+ .container {
+ flex-direction: column;
+ }
+
+ .parameter-area {
+ margin: -5px;
+ }
+}
+
+/* Style pour le header et la navigation mobile */
+.parameter-header {
+ background-color: #34495e;
+ padding: 20px 0;
+ margin: 0;
+ width: 100%;
+}
+
+.mobile-nav {
+ display: none; /* Par défaut caché */
+ width: 100%;
+ padding: 10px;
+ background-color: #34495e;
+ overflow-x: auto;
+}
+
+.mobile-nav ul {
+ display: flex;
+ gap: 10px;
+ margin: 0;
+ padding: 10px;
+ list-style: none;
+}
+
+.mobile-nav li {
+ flex: 0 0 auto;
+ white-space: nowrap;
+}
+
+/* Ajoutez ces styles pour la bannière dans la popup */
+.banner-container {
+ width: 100%;
+ margin-bottom: 20px;
+}
+
+.banner-wrapper {
+ width: 100%;
+ height: 120px;
+ overflow: hidden;
+ position: relative;
+ border-radius: 10px;
+}
+
+.banner-wrapper img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+/* Mise à jour des styles existants */
+.popup-content {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: white;
+ padding: 20px;
+ border-radius: 10px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ width: 90%;
+ max-width: 500px;
+ max-height: 80vh;
+ overflow-y: auto;
+}
+
+/* Style pour le conteneur de la bannière */
+.banner-upload-label {
+ display: block;
+ width: auto;
+ padding: 12px 20px;
+ background-color: var(--accent-color);
+ color: white;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ text-align: center;
+ font-size: 16px;
+ margin: 10px auto;
+ max-width: 200px;
+}
+
+/* ---------------------Style pour la popup de contrat--------------------- */
+
+.contract-popup-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex
+;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+.contract-popup-content {
+ background: white;
+ padding: 30px;
+ border-radius: 8px;
+ max-width: 600px;
+ width: 90%;
+ max-height: 80vh;
+ overflow-y: auto;
+ position: relative;
+}
+
+.close-contract-popup {
+ position: absolute;
+ top: 15px;
+ right: 15px;
+ background: none;
+ border: none;
+ font-size: 24px;
+ cursor: pointer;
+ color: #666;
+}
+
+/* Style pour la popup d'alerte */
+.alert-popup {
+ position: fixed;
+ top: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ background-color: #f44336;
+ color: white;
+ padding: 15px 25px;
+ border-radius: 4px;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
+ z-index: 1000;
+ display: none;
+ animation: slideDown 0.3s ease-out;
+}
+
+/* ---------------------Style pour la popup notification--------------------- */
+
+
+.notifications-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+.notifications-content {
+ position: relative;
+ background: white;
+ border-radius: 8px;
+ padding: 24px;
+ width: 90%;
+ max-width: 500px;
+}
+
+.close-button {
+ position: absolute;
+ top: 15px;
+ right: 20px;
+ font-size: 24px;
+ color: #666;
+ cursor: pointer;
+ transition: color 0.2s;
+}
+
+.close-button:hover {
+ color: #000;
+}
+
+.notifications-title {
+ padding-right: 30px; /* Pour éviter que le titre ne chevauche le bouton de fermeture */
+}
+
+.notifications-title {
+ font-size: 24px;
+ color: #445B6E;
+ margin-bottom: 20px;
+ font-weight: 500;
+}
+
+.notifications-list {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.notification-item {
+ display: flex;
+ align-items: flex-start;
+ padding: 12px 0;
+ border-bottom: 1px solid #eee;
+ cursor: pointer;
+}
+
+.notification-status {
+ margin-right: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+}
+
+.dot-icon, .check-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+}
+
+.notification-content {
+ flex: 1;
+}
+
+.notification-message {
+ font-size: 16px;
+ color: #333;
+ margin-bottom: 4px;
+}
+
+.notification-date {
+ font-size: 14px;
+ color: #666;
+}
+
+.notification-item:hover {
+ background-color: #f8f9fa;
+}
+
+.notification-item.read {
+ opacity: 0.7;
+}
+
+.notification-item.unread {
+ background-color: #fff;
+}
+
+.close-notifications {
+ position: absolute;
+ top: 15px;
+ right: 15px;
+ border: none;
+ background: none;
+ font-size: 24px;
+ color: #666;
+ cursor: pointer;
+}
+
+/*-------------------------------------- STYLE ACTION BUTTON ---------------------*/
+
+.action-buttons-wrapper {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ justify-content: center;
+ margin: 20px 0;
+}
+
+.action-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0;
+ border-radius: 8px;
+ border: none;
+ font-size: 16px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ color: white;
+ height: 40px;
+ width: 40px;
+}
+
+.confirm-button {
+ background-color: #4CAF50;
+}
+
+.confirm-button:hover {
+ background-color: #3d8b40;
+ transform: translateY(-2px);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+}
+
+.cancel-button {
+ background-color: rgb(244, 67, 54);
+}
+
+.cancel-button:hover {
+ background-color: #d32f2f;
+ transform: translateY(-2px);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+}
+
+.banner-image.clickable {
+ cursor: pointer;
+ transition: opacity 0.3s ease;
+}
+
+.banner-image.clickable:hover {
+ opacity: 0.8;
+}
diff --git a/public/style/chat.css b/public/style/chat.css
new file mode 100644
index 0000000..8ab120b
--- /dev/null
+++ b/public/style/chat.css
@@ -0,0 +1,337 @@
+/* Styles de base */
+:root {
+ --primary-color: #3A506B;
+ /* Bleu métallique */
+ --secondary-color: #B0BEC5;
+ /* Gris acier */
+ --accent-color: #D68C45;
+ /* Cuivre */
+}
+
+body {
+ font-family: Arial, sans-serif;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ height: 100vh;
+ background-color: #e9edf1;
+ flex-direction: column;
+}
+
+
+/* 4NK NAVBAR */
+
+.brand-logo {
+ text-align: center;
+ font-size: 1.5em;
+ font-weight: bold;
+}
+
+.nav-wrapper {
+ position: fixed;
+ background: radial-gradient(circle, white, var(--primary-color));
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ color: #37474F;
+ height: 9vh;
+ width: 100vw;
+ left: 0;
+ top: 0;
+ 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);
+}
+
+/* Icônes de la barre de navigation */
+.nav-right-icons {
+ display: flex;
+}
+
+.notification-bell,
+.burger-menu {
+ height: 20px;
+ width: 20px;
+ margin-right: 1rem;
+ cursor: pointer;
+}
+
+.notification-container {
+ position: relative;
+ /* Conserve la position pour le notification-board */
+ display: inline-flex;
+ align-items: center;
+}
+
+.notification-board {
+ position: absolute;
+ /* Position absolue pour le placer par rapport au container */
+ top: 40px;
+ right: 0;
+ background-color: white;
+ border: 1px solid #ccc;
+ padding: 10px;
+ width: 200px;
+ max-height: 300px;
+ overflow-y: auto;
+ /* Scroll si les notifications dépassent la taille */
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ z-index: 10;
+ /* Définit la priorité d'affichage au-dessus des autres éléments */
+ display: none;
+ /* Par défaut, la notification est masquée */
+}
+
+.notification-item{
+ cursor: pointer;
+}
+
+.notification-badge {
+ position: absolute;
+ top: -18px;
+ right: 35px;
+ background-color: red;
+ color: white;
+ border-radius: 50%;
+ padding: 4px 8px;
+ font-size: 12px;
+ display: none;
+ /* S'affiche seulement lorsqu'il y a des notifications */
+ z-index: 10;
+}
+
+/* Par défaut, le menu est masqué */
+#menu {
+ display: none;
+ /* Menu caché par défaut */
+ transition: display 0.3s ease-in-out;
+}
+
+.burger-menu {
+ cursor: pointer;
+}
+
+/* Icône burger */
+#burger-icon {
+ cursor: pointer;
+}
+
+.menu-content {
+ display: none;
+ position: absolute;
+ top: 3.4rem;
+ right: 1rem;
+ background-color: white;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 5px;
+ overflow: hidden;
+}
+
+.menu-content a {
+ display: block;
+ padding: 10px 20px;
+ text-decoration: none;
+ color: #333;
+ border-bottom: 1px solid #e0e0e0;
+
+ &:hover {
+ background-color: rgba(26, 28, 24, .08);
+ }
+}
+
+.menu-content a:last-child {
+ border-bottom: none;
+}
+
+/* Ajustement pour la barre de navigation fixe */
+.container {
+ display: flex;
+ flex: 1;
+ height: calc(100% - 4vh);
+ margin-top: 4vh;
+ margin-left: -1%;
+ text-align: left;
+}
+
+
+/* Liste des groupes */
+
+.group-list {
+ width: 25%;
+ background-color: #1f2c3d;
+ color: white;
+ padding: 20px;
+ box-sizing: border-box;
+ overflow-y: auto;
+ border-right: 2px solid #2c3e50;
+ flex-shrink: 0;
+ padding-right: 10px;
+}
+
+.group-list ul {
+ cursor: pointer;
+ list-style: none;
+ padding: 0;
+ padding-right: 10px;
+ margin-left: 20px;
+}
+
+.group-list li {
+ margin-bottom: 20px;
+ padding: 15px;
+ border-radius: 8px;
+ background-color: #273646;
+ cursor: pointer;
+ transition: background-color 0.3s, box-shadow 0.3s;
+}
+
+.group-list li:hover {
+ background-color: #34495e;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+}
+
+
+/* Zone de chat */
+.chat-area {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-width: 0;
+ background-color: #ffffff;
+ border-radius: 10px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+ margin: 10px;
+ margin-top: 20px;
+ margin-left: 0%;
+}
+
+/* En-tête du chat */
+.chat-header {
+ background-color: #34495e;
+ color: white;
+ padding: 15px;
+ font-size: 20px;
+ font-weight: bold;
+ border-radius: 10px 10px 0 0;
+ text-align: center;
+}
+
+/* Messages */
+.messages {
+ flex: 1;
+ padding: 20px;
+ overflow-y: auto;
+ background-color: #f1f1f1;
+ border-top: 1px solid #ddd;
+}
+
+.message-container {
+ max-width: 100%;
+ border-radius: 5px;
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+ background-color: #f1f1f1;
+ display: flex;
+ flex-direction: column;
+}
+
+.message-container .message {
+ align-self: flex-start;
+}
+
+.message-container .message.user {
+ align-self: flex-end;
+ margin-left: auto;
+ color: white;
+}
+
+.message {
+ padding: 12px 18px;
+ background-color: #e1e1e1;
+ border-radius: 15px;
+ max-width: 70%;
+ font-size: 16px;
+ line-height: 1.4;
+ margin-bottom: 0%;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ position: relative;
+ display: inline-block;
+}
+
+/* Messages de l'utilisateur */
+.message.user {
+ background-color: #3498db;
+ color: white;
+ align-self: flex-end;
+ text-align: right;
+}
+
+/* Amélioration de l'esthétique des messages */
+/* .message.user:before {
+ content: '';
+ position: absolute;
+ top: 10px;
+ right: -10px;
+ border: 10px solid transparent;
+ border-left-color: #3498db;
+} */
+
+/* Zone de saisie */
+.input-area {
+ padding: 10px;
+ background-color: #bdc3c7;
+ display: flex;
+ align-items: center;
+ /* Alignement vertical */
+}
+
+.input-area input[type="text"] {
+ flex: 1;
+ /* Prend l'espace restant */
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+}
+
+.input-area .attachment-icon {
+ margin: 0 10px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+}
+
+.input-area button {
+ padding: 10px;
+ margin-left: 10px;
+ background-color: #2980b9;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+.input-area button:hover {
+ background-color: #1f608d;
+}
+
+#message-input {
+ width: 100%;
+ height: 50px;
+ resize: none;
+ padding: 10px;
+ box-sizing: border-box;
+ overflow: auto;
+ max-width: 100%;
+ border-radius: 10px;
+}
+
+/* Responsive */
+@media screen and (max-width: 768px) {
+ .group-list {
+ display: none;
+ /* Masquer la liste des groupes sur les petits écrans */
+ }
+
+ .chat-area {
+ margin: 0;
+ }
+}
\ No newline at end of file
diff --git a/public/style/signature.css b/public/style/signature.css
new file mode 100644
index 0000000..84a8c2b
--- /dev/null
+++ b/public/style/signature.css
@@ -0,0 +1,1485 @@
+/* Styles de base */
+:root {
+ --primary-color: #3A506B;
+ /* Bleu métallique */
+ --secondary-color: #B0BEC5;
+ /* Gris acier */
+ --accent-color: #D68C45;
+ /* Cuivre */
+}
+
+body {
+ font-family: Arial, sans-serif;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ height: 100vh;
+ background-color: #e9edf1;
+ flex-direction: column;
+}
+
+/* 4NK NAVBAR */
+
+.brand-logo {
+ text-align: center;
+ font-size: 1.5em;
+ font-weight: bold;
+}
+
+.nav-wrapper {
+ position: fixed;
+ background: radial-gradient(circle, white, var(--primary-color));
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ color: #37474F;
+ height: 9vh;
+ width: 100vw;
+ left: 0;
+ top: 0;
+ 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);
+}
+
+/* Icônes de la barre de navigation */
+.nav-right-icons {
+ display: flex;
+}
+
+.notification-bell,
+.burger-menu {
+ height: 20px;
+ width: 20px;
+ margin-right: 1rem;
+ cursor: pointer;
+}
+
+.notification-container {
+ position: relative;
+ /* Conserve la position pour le notification-board */
+ display: inline-flex;
+ align-items: center;
+}
+
+.notification-board {
+ position: absolute;
+ /* Position absolue pour le placer par rapport au container */
+ top: 40px;
+ right: 0;
+ background-color: white;
+ border: 1px solid #ccc;
+ padding: 10px;
+ width: 200px;
+ max-height: 300px;
+ overflow-y: auto;
+ /* Scroll si les notifications dépassent la taille */
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ z-index: 10;
+ /* Définit la priorité d'affichage au-dessus des autres éléments */
+ display: none;
+ /* Par défaut, la notification est masquée */
+}
+
+.notification-item{
+ cursor: pointer;
+}
+
+.notification-badge {
+ position: absolute;
+ top: -18px;
+ right: 35px;
+ background-color: red;
+ color: white;
+ border-radius: 50%;
+ padding: 4px 8px;
+ font-size: 12px;
+ display: none;
+ /* S'affiche seulement lorsqu'il y a des notifications */
+ z-index: 10;
+}
+
+/* Par défaut, le menu est masqué */
+#menu {
+ display: none;
+ /* Menu caché par défaut */
+ transition: display 0.3s ease-in-out;
+}
+
+.burger-menu {
+ cursor: pointer;
+}
+
+/* Icône burger */
+#burger-icon {
+ cursor: pointer;
+}
+
+.menu-content {
+ display: none;
+ position: absolute;
+ top: 3.4rem;
+ right: 1rem;
+ background-color: white;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 5px;
+ overflow: hidden;
+}
+
+.menu-content a {
+ display: block;
+ padding: 10px 20px;
+ text-decoration: none;
+ color: #333;
+ border-bottom: 1px solid #e0e0e0;
+
+ &:hover {
+ background-color: rgba(26, 28, 24, .08);
+ }
+}
+
+.menu-content a:last-child {
+ border-bottom: none;
+}
+
+/* Ajustement pour la barre de navigation fixe */
+.container {
+ display: flex;
+ flex: 1;
+ height: calc(100% - 4vh);
+ margin-top: 4vh;
+ margin-left: -1%;
+ text-align: left;
+}
+
+
+/* Liste des groupes */
+
+.group-list {
+ width: 25%;
+ background-color: #1f2c3d;
+ color: white;
+ padding: 20px;
+ box-sizing: border-box;
+ overflow-y: auto;
+ border-right: 2px solid #2c3e50;
+ flex-shrink: 0;
+ padding-right: 10px;
+}
+
+.group-list ul {
+ cursor: pointer;
+ list-style: none;
+ padding: 0;
+ padding-right: 10px;
+ margin-left: 20px;
+}
+
+.group-list li {
+ margin-bottom: 10px;
+ padding: 15px;
+ border-radius: 8px;
+ background-color: #273646;
+ cursor: pointer;
+ transition: background-color 0.3s, box-shadow 0.3s;
+}
+
+.group-list li:hover {
+ background-color: #34495e;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+}
+
+
+/* Zone de chat */
+
+.chat-area {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-width: 0;
+ background-color: #ffffff;
+ border-radius: 10px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+ margin: 10px;
+ margin-top: 20px;
+ margin-left: 0%;
+}
+
+/* En-tête du chat */
+.chat-header {
+ background-color: #34495e;
+ color: white;
+ padding: 15px;
+ font-size: 20px;
+ font-weight: bold;
+ border-radius: 10px 10px 0 0;
+ text-align: center;
+}
+
+/* Messages */
+.messages {
+ flex: 1;
+ padding: 20px;
+ overflow-y: auto;
+ background-color: #f1f1f1;
+ border-top: 1px solid #ddd;
+}
+
+.message-container {
+ max-width: 100%;
+ border-radius: 5px;
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+ background-color: #f1f1f1;
+ display: flex;
+ flex-direction: column;
+}
+
+.message-container .message {
+ align-self: flex-start;
+}
+
+.message-container .message.user {
+ align-self: flex-end;
+ margin-left: auto;
+ color: white;
+}
+
+.message {
+ padding: 12px 18px;
+ background-color: #e1e1e1;
+ border-radius: 15px;
+ max-width: 70%;
+ font-size: 16px;
+ line-height: 1.4;
+ margin-bottom: 0%;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ position: relative;
+ display: inline-block;
+}
+
+/* Messages de l'utilisateur */
+.message.user {
+ background-color: #3498db;
+ color: white;
+ align-self: flex-end;
+ text-align: right;
+}
+
+/* Amélioration de l'esthétique des messages */
+/* .message.user:before {
+ content: '';
+ position: absolute;
+ top: 10px;
+ right: -10px;
+ border: 10px solid transparent;
+ border-left-color: #3498db;
+} */
+
+/* Zone de saisie */
+.input-area {
+ padding: 10px;
+ background-color: #bdc3c7;
+ display: flex;
+ align-items: center;
+ /* Alignement vertical */
+}
+
+.input-area input[type="text"] {
+ flex: 1;
+ /* Prend l'espace restant */
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+}
+
+.input-area .attachment-icon {
+ margin: 0 10px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+}
+
+.input-area button {
+ padding: 10px;
+ margin-left: 10px;
+ background-color: #2980b9;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+.input-area button:hover {
+ background-color: #1f608d;
+}
+
+#message-input {
+ width: 100%;
+ height: 50px;
+ resize: none;
+ padding: 10px;
+ box-sizing: border-box;
+ overflow: auto;
+ max-width: 100%;
+ border-radius: 10px;
+}
+
+/* Responsive */
+@media screen and (max-width: 768px) {
+ .group-list {
+ display: none;
+ /* Masquer la liste des groupes sur les petits écrans */
+ }
+
+ .chat-area {
+ margin: 0;
+ }
+}
+
+#process-details {
+ flex: 1;
+ background: white;
+ border-radius: 8px;
+ margin: 10px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ display: none;
+ overflow: hidden;
+}
+
+.process-details-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px;
+ background: #f8f9fa;
+ border-bottom: 1px solid #eee;
+ border-radius: 8px 8px 0 0;
+}
+
+.process-details-header h2 {
+ margin: 0;
+ color: #333;
+}
+
+.close-btn {
+ background: none;
+ border: none;
+ font-size: 24px;
+ cursor: pointer;
+ color: #666;
+}
+
+.close-btn:hover {
+ color: #333;
+}
+
+.process-details-content {
+ padding: 20px;
+ overflow-y: auto;
+ height: calc(100% - 70px);
+}
+
+.details-section {
+ margin-bottom: 30px;
+}
+
+.details-section h3 {
+ color: #444;
+ margin-bottom: 15px;
+ padding-bottom: 8px;
+ border-bottom: 2px solid #f0f0f0;
+}
+
+.documents-list {
+ list-style: none;
+ padding: 0;
+}
+
+.documents-list li {
+ padding: 8px 0;
+ border-bottom: 1px solid #eee;
+}
+
+.roles-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 20px;
+}
+
+.role-block {
+ background: #f8f9fa;
+ border-radius: 8px;
+ padding: 15px;
+ border: 1px solid #eee;
+}
+
+.role-block h4 {
+ color: #555;
+ margin: 0 0 10px 0;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #eee;
+}
+
+.members-list {
+ list-style: none;
+ padding: 0;
+}
+
+.members-list li {
+ padding: 8px 12px;
+ margin: 4px 0;
+ cursor: pointer;
+ border-radius: 4px;
+ transition: background-color 0.2s;
+}
+
+.members-list li:hover {
+ background-color: #e9ecef;
+}
+
+.group-list-item {
+ padding: 8px 16px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.group-item-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+}
+
+.process-name {
+ flex-grow: 1;
+ cursor: pointer;
+}
+
+.settings-icon {
+ cursor: pointer;
+ padding: 5px 8px;
+ margin-left: 10px;
+ border-radius: 4px;
+}
+
+.settings-icon:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+}
+
+.process-details {
+ position: fixed;
+ top: 9vh;
+ right: 0;
+ bottom: 0;
+ left: 23.5%;
+ background-color: white;
+ box-sizing: border-box;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ border-radius: 8px;
+ margin: 10px;
+ z-index: 990;
+}
+
+.process-details-content {
+ height: calc(100% - 60px); /* Ajusté pour tenir compte du header */
+ overflow-y: auto;
+ padding: 20px;
+}
+
+.documents-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 20px;
+ margin-top: 15px;
+}
+
+.document-card {
+ background: white;
+ border-radius: 8px;
+ padding: 15px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ border: 1px solid #eee;
+}
+
+.document-card.public {
+ border-left: 4px solid #4CAF50;
+}
+
+.document-card.private {
+ border-left: 4px solid #FFC107;
+}
+
+.document-card.confidential {
+ border-left: 4px solid #F44336;
+}
+
+.document-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #eee;
+}
+
+.document-header h4 {
+ margin: 0;
+ color: #333;
+}
+
+.document-visibility {
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+ font-weight: bold;
+}
+
+.public .document-visibility {
+ background-color: #E8F5E9;
+ color: #2E7D32;
+}
+
+.private .document-visibility {
+ background-color: #FFF3E0;
+ color: #F57C00;
+}
+
+.confidential .document-visibility {
+ background-color: #FFEBEE;
+ color: #C62828;
+}
+
+.document-info {
+ margin: 10px 0;
+ font-size: 14px;
+ color: #666;
+}
+
+.document-info p {
+ margin: 5px 0;
+}
+
+.signatures-list {
+ margin-top: 10px;
+}
+
+.signature-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px;
+ border-radius: 4px;
+ margin: 5px 0;
+ background-color: #f8f9fa;
+}
+
+.signature-item.signed {
+ background-color: #E8F5E9;
+}
+
+.signature-item.pending {
+ background-color: #FFF3E0;
+}
+
+.signer-name {
+ font-weight: 500;
+}
+
+.signature-status {
+ font-size: 12px;
+}
+
+.signed .signature-status {
+ color: #2E7D32;
+}
+
+.pending .signature-status {
+ color: #F57C00;
+}
+
+.user-selector {
+ position: relative;
+ margin-right: 20px;
+}
+
+#userSwitchBtn {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 8px 12px;
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ color: var(--primary-color);
+}
+
+#userSwitchBtn:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.current-user-info {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.user-avatar {
+ background-color: var(--primary-color);
+ color: white;
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: bold;
+}
+
+.user-list {
+ position: absolute;
+ top: 100%;
+ right: 0;
+ background: white;
+ border-radius: 4px;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+ display: none;
+ z-index: 1000;
+ max-height: 400px;
+ overflow-y: auto;
+ width: 250px;
+}
+
+.user-list.show {
+ display: block;
+}
+
+.user-list-item {
+ padding: 8px 16px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+}
+
+.user-list-item:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.user-list-item .user-avatar {
+ width: 24px;
+ height: 24px;
+ font-size: 12px;
+}
+
+.user-list-item .user-email {
+ font-size: 12px;
+ color: #666;
+ display: block;
+}
+
+.document-card {
+ margin-bottom: 20px;
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+}
+
+.progress-bar {
+ background-color: #f3f3f3;
+ border-radius: 5px;
+ height: 10px;
+ width: 100%;
+ margin-top: 5px;
+}
+
+.progress {
+ background-color: #4caf50; /* Couleur de la barre de progression */
+ height: 100%;
+ border-radius: 5px;
+}
+
+.new-request-btn {
+ background-color: #4caf50;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ padding: 10px 15px;
+ cursor: pointer;
+ margin-left: 10px;
+}
+
+.new-request-btn:hover {
+ background-color: #45a049;
+}
+
+.header-buttons {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.close-btn {
+ background: none;
+ border: none;
+ font-size: 24px;
+ cursor: pointer;
+ color: #666;
+}
+
+.close-btn:hover {
+ color: #333;
+}
+
+.new-request-view {
+ padding: 20px;
+ background-color: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ margin: 20px;
+}
+
+.upload-area {
+ border: 2px dashed #ccc;
+ padding: 20px;
+ text-align: center;
+ margin: 20px 0;
+}
+
+.upload-icon {
+ font-size: 50px;
+ margin: 10px 0;
+}
+
+/* New Request View */
+.new-request-view {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: white;
+ z-index: 1000; /* Valeur élevée pour s'assurer qu'il est au-dessus des autres éléments */
+ padding: 20px;
+ box-sizing: border-box;
+ overflow-y: auto;
+}
+
+.upload-area {
+ border: 2px dashed #ccc;
+ padding: 40px;
+ text-align: center;
+ margin: 20px auto;
+ max-width: 600px;
+ background-color: #f9f9f9;
+ border-radius: 8px;
+ cursor: pointer;
+}
+
+.upload-icon {
+ font-size: 50px;
+ margin: 20px 0;
+}
+
+.new-request-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 60px;
+ padding: 0 20px;
+}
+
+.upload-area {
+ border: 2px dashed #ccc;
+ padding: 40px;
+ text-align: center;
+ margin: 20px auto;
+ max-width: 600px;
+ background-color: #f9f9f9;
+ border-radius: 8px;
+ cursor: pointer;
+}
+
+.details-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: #f8f9fa;
+ padding: 5px 20px;
+ border-radius: 8px 8px 0 0;
+}
+
+.header-buttons {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-direction: row;
+}
+
+.close-btn {
+ background: none;
+ border: none;
+ font-size: 24px;
+ cursor: pointer;
+ color: #666;
+ padding: 5px;
+}
+
+.close-btn:hover {
+ color: #333;
+}
+
+.new-request-btn {
+ background-color: #4caf50;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ padding: 8px 16px;
+ cursor: pointer;
+ /* margin-left: 10px; <- Supprimez cette ligne si elle existe */
+}
+
+.new-request-btn:hover {
+ background-color: #45a049;
+}
+
+/* Ajoutez ces styles à votre fichier CSS existant */
+.document-card.vierge {
+ background-color: #fff3cd;
+ border: 2px solid #ffeeba;
+}
+
+.document-card.vierge .document-header {
+ background-color: #fff3cd;
+}
+
+.document-card.vierge h4 {
+ color: #856404;
+}
+
+.document-card.vierge .document-info {
+ color: #856404;
+}
+
+/* Style pour l'emoji d'avertissement */
+.document-card.vierge h4::before {
+ margin-right: 8px;
+}
+
+.vierge-documents-container {
+ padding: 20px;
+ overflow-y: auto;
+ max-height: calc(100vh - 150px);
+}
+
+.document-form {
+ padding: 15px;
+ background-color: #fff;
+ border-radius: 0 0 5px 5px;
+ display: flex;
+ gap: 20px;
+}
+
+.form-left {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+}
+
+.form-right {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end; /* Aligner le bouton à droite */
+}
+
+.form-group {
+ flex: 2;
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 15px;
+}
+
+.form-group-members {
+ flex: 2;
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 15px;
+ font-weight: bold;
+}
+
+.submit-btn {
+ background-color: #4caf50;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ padding: 10px 15px;
+ cursor: pointer;
+ margin-left: 47%;
+ margin-top: 20px;
+}
+
+.submit-btn:hover {
+ background-color: #45a049;
+}
+
+.upload-format {
+ font-size: 12px;
+ color: #666;
+ margin: 5px 0;
+}
+
+.browse-btn {
+ background-color: #4caf50;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ margin-top: 10px;
+}
+
+.browse-btn:hover {
+ background-color: #45a049;
+}
+
+.document-selector {
+ padding: 20px;
+ margin-bottom: 20px;
+}
+
+.document-selector label {
+ display: block;
+ margin-bottom: 10px;
+ font-weight: bold;
+ color: #666;
+}
+
+.document-selector select {
+ width: 100%;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 16px;
+ background-color: white;
+}
+
+#selected-document-form {
+ padding: 0 20px;
+ overflow: auto;
+ height: 65%;
+}
+
+/* Style pour l'option avec l'emoji */
+.document-selector select option {
+ padding: 10px;
+ font-size: 14px;
+}
+
+.members-selection {
+ max-height: 200px;
+ overflow-y: auto;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ padding: 10px;
+ background-color: white;
+}
+
+.member-checkbox {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ padding: 5px;
+}
+
+.member-checkbox:hover {
+ background-color: #f5f5f5;
+}
+
+.member-checkbox input[type="checkbox"] {
+ margin-right: 10px;
+}
+
+.member-checkbox label {
+ cursor: pointer;
+ flex: 1;
+}
+
+/* Style pour le conteneur des membres */
+.members-selection-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center; /* Centrer la liste des membres */
+ width: 100%;
+}
+
+#members-list {
+ width: 60%;
+ height: 100%;
+ margin-bottom: -40px;
+ margin-top: 10px;
+}
+
+/* Style pour le label des membres */
+.members-selection-container label {
+ font-weight: bold; /* Mettre le texte en gras */
+ margin-bottom: 10px; /* Espacement en bas */
+ display: block; /* Affichage en bloc */
+}
+
+/* Style pour les cases à cocher des membres */
+.member-checkbox {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ padding: 5px;
+ border-radius: 4px; /* Coins arrondis */
+ transition: background-color 0.2s; /* Transition pour l'effet de survol */
+}
+
+.member-checkbox:hover {
+ background-color: #e9ecef; /* Couleur de fond au survol */
+}
+
+/* Style pour les labels */
+.form-group label {
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+/* Style pour les champs de saisie */
+.form-group input,
+.form-group select {
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 16px;
+ width: 80%;
+}
+
+.add-members-btn {
+ background-color: #4caf50;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ padding: 10px 15px;
+ cursor: pointer;
+ margin-top: 10px; /* Espacement au-dessus du bouton */
+}
+
+.add-members-btn:hover {
+ background-color: #45a049;
+}
+
+/* Styles pour l'overlay et la modale */
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 999;
+}
+
+.modal {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background-color: white;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ z-index: 1000;
+ width: 25%;
+ max-width: 500px;
+}
+
+.modal-content {
+ max-height: 70vh;
+ overflow-y: auto;
+}
+
+.modal-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ margin-top: 20px;
+ padding-top: 10px;
+ border-top: 1px solid #eee;
+}
+
+.confirm-btn {
+ background-color: #4caf50;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.confirm-btn:hover {
+ background-color: #45a049;
+}
+
+.selected-member {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: #f5f5f5;
+ padding: 8px 12px;
+ margin: 4px 0;
+ border-radius: 4px;
+}
+
+.cancel-btn {
+ background-color: #df2020;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.cancel-btn:hover {
+ background-color: #c62828; /
+}
+
+.remove-member-btn {
+ background-color: #dc3545;
+ color: white;
+ border: none;
+ border-radius: 50%;
+ width: 24px;
+ height: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ font-size: 16px;
+ padding: 0;
+ margin-left: 8px;
+}
+
+.remove-member-btn:hover {
+ background-color: #c82333;
+}
+
+#description {
+ height: 100px;
+ width: 100%;
+ border-radius: 20px;
+ padding: 10px 0;
+ resize: none;
+
+}
+
+.role-item-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ padding: 5px 0;
+}
+
+.role-item-container span:last-child {
+ margin-left: 10px;
+}
+
+.document-card .new-request-btn {
+ margin-top: 60%;
+ margin-left: 65%;
+ padding: 6px 12px;
+ background-color: #4CAF50;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 0.9em;
+}
+
+.document-card .new-request-btn:hover {
+ background-color: #45a049;
+}
+
+/* Styles pour la modale de nouveau document */
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+
+.modal-content-document {
+ background: white;
+ padding: 20px;
+ border-radius: 8px;
+ width: 90%;
+ max-width: 600px;
+ max-height: 90vh;
+ overflow-y: auto;
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #eee;
+}
+
+.modal-header h2 {
+ margin: 0;
+ color: #333;
+}
+
+.modal-body {
+ margin-bottom: 20px;
+}
+
+.modal-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding-top: 20px;
+ border-top: 1px solid #eee;
+}
+
+.modal-document {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+.form-group {
+ margin-bottom: 15px;
+}
+
+.form-group label {
+ display: block;
+ margin-bottom: 5px;
+ font-weight: bold;
+ color: #555;
+}
+
+.form-control {
+ width: 100%;
+ padding: 8px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 14px;
+}
+
+.form-row {
+ display: flex;
+ gap: 15px;
+ margin-bottom: 15px;
+}
+
+.form-group.half {
+ flex: 1;
+ margin-bottom: 0; /* Annule la marge du form-group standard */
+}
+
+.selected-signatories {
+ margin: 10px 0;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ min-height: 50px;
+}
+
+.signatory-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 5px;
+ margin: 5px 0;
+ background: #f5f5f5;
+ border-radius: 4px;
+}
+
+.remove-btn {
+ background: none;
+ border: none;
+ color: #dc3545;
+ cursor: pointer;
+ font-size: 18px;
+ padding: 0 5px;
+}
+
+.remove-btn:hover {
+ color: #bd2130;
+}
+
+.btn-primary {
+ background: #4CAF50;
+ color: white;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.btn-primary:hover {
+ background: #45a049;
+}
+
+.btn-secondary {
+ background: #6c757d;
+ color: white;
+ padding: 8px 15px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.btn-secondary:hover {
+ background: #5a6268;
+}
+
+.signatories-list {
+ max-height: 300px;
+ overflow-y: auto;
+}
+
+.signatory-option {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px;
+ border-bottom: 1px solid #eee;
+}
+
+.role-select {
+ padding: 4px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ margin-left: auto;
+}
+
+.role-section {
+ margin-bottom: 20px;
+ padding: 10px;
+ background: #f8f9fa;
+ border-radius: 4px;
+}
+
+.role-section h4 {
+ margin: 0 0 10px 0;
+ color: #495057;
+}
+
+.members-selection {
+ max-height: 300px;
+ overflow-y: auto;
+ padding: 10px;
+ border: 1px solid #dee2e6;
+ border-radius: 4px;
+}
+
+input[type="file"] {
+ padding: 10px;
+ border: 1px dashed #ccc;
+ border-radius: 4px;
+ width: 100%;
+ margin-top: 5px;
+}
+
+.file-upload-container {
+ border: 2px dashed #ccc;
+ padding: 20px;
+ text-align: center;
+ margin: 10px 0;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+.file-upload-container:hover {
+ background-color: #f5f5f5;
+ border-color: #666;
+}
+
+.file-upload-container.dragover {
+ background-color: #f0f0f0;
+ border-color: #4CAF50;
+}
+
+.file-list {
+ margin-top: 10px;
+}
+
+.file-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px;
+ margin: 5px 0;
+ background: #f5f5f5;
+ border-radius: 4px;
+}
+
+.file-info {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
+
+.remove-file {
+ background: none;
+ border: none;
+ color: #ff4444;
+ cursor: pointer;
+ font-size: 18px;
+}
+
+#fileInput {
+ display: none;
+}
+
+.required-signatories {
+ margin: 10px 0;
+}
+
+.signatory-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px;
+ margin: 5px 0;
+ background: #f5f5f5;
+ border-radius: 4px;
+}
+
+.signatory-item.locked {
+ background: #e8e8e8;
+ cursor: not-allowed;
+}
+
+.member-name {
+ font-weight: 500;
+}
+
+.role-info {
+ color: #666;
+ font-size: 0.9em;
+}
+
+.lock-icon {
+ margin-left: auto;
+ opacity: 0.6;
+}
diff --git a/src/components/profile-header/profile-header.html b/src/components/profile-header/profile-header.html
new file mode 100644
index 0000000..51fd69d
--- /dev/null
+++ b/src/components/profile-header/profile-header.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+ John
+ Doe
+
+
+
\ No newline at end of file
diff --git a/src/mocks/mock-chat/groupsMock.js b/src/mocks/mock-chat/groupsMock.js
new file mode 100644
index 0000000..d567e26
--- /dev/null
+++ b/src/mocks/mock-chat/groupsMock.js
@@ -0,0 +1,40 @@
+export const groupsMock = [
+ {
+ id: 1,
+ name: "Group 🚀 ",
+ roles: [
+ {
+ id: 1,
+ name: "Role 1",
+ members: [{ id: 1, name: "Member 1" }, { id: 2, name: "Member 2" }]
+ },
+ {
+ id: 2,
+ name: "Role 2",
+ members: [{ id: 3, name: "Member 3" }, { id: 4, name: "Member 4" }]
+ }
+ ]
+ },
+ {
+ id: 2,
+ name: "Group ₿",
+ roles: [
+ {
+ id: 3,
+ name: "Role 1",
+ members: [{ id: 5, name: "Member 5" }, { id: 6, name: "Member 6" }]
+ }
+ ]
+ },
+ {
+ id: 3,
+ name: "Group 🪙",
+ roles: [
+ {
+ id: 4,
+ name: "Role 1",
+ members: [{ id: 7, name: "Member 7" }, { id: 8, name: "Member 8" }]
+ }
+ ]
+ }
+];
diff --git a/src/mocks/mock-chat/messagesMock.js b/src/mocks/mock-chat/messagesMock.js
new file mode 100644
index 0000000..dfd85c7
--- /dev/null
+++ b/src/mocks/mock-chat/messagesMock.js
@@ -0,0 +1,65 @@
+export const messagesMock = [
+ {
+ memberId: 1, // Conversations avec Mmber 1
+ messages: [
+ { id: 1, sender: "Member 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" }
+ ]
+ }
+
+];
diff --git a/src/mocks/mock-signature/groupsMock.js b/src/mocks/mock-signature/groupsMock.js
new file mode 100644
index 0000000..6959020
--- /dev/null
+++ b/src/mocks/mock-signature/groupsMock.js
@@ -0,0 +1,264 @@
+// Définir les rôles autorisés
+const VALID_ROLES = [
+ "User",
+ "Process",
+ "Member",
+ "Peer",
+ "Payment",
+ "Deposit",
+ "Artefact",
+ "Resolve",
+ "Backup"
+];
+
+const VISIBILITY_LEVELS = {
+ PUBLIC: "public",
+ CONFIDENTIAL: "confidential",
+ PRIVATE: "private",
+};
+
+const DOCUMENT_STATUS = {
+ DRAFT: "draft",
+ PENDING: "pending",
+ IN_REVIEW: "in_review",
+ APPROVED: "approved",
+ REJECTED: "rejected",
+ EXPIRED: "expired"
+};
+
+// Fonction pour créer un rôle
+function createRole(name, members) {
+ if (!VALID_ROLES.includes(name)) {
+ throw new Error(`Role "${name}" is not valid.`);
+ }
+ return { name, members };
+}
+
+export const groupsMock = [
+ {
+ id: 1,
+ name: "Processus 1",
+ description: "Description du processus 1",
+ 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: []
+ }
+ ]
+ },
+ {
+ 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",
+ 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",
+ 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: []
+ }
+ ]
+ }
+ ]
+ }
+];
diff --git a/src/mocks/mock-signature/membersMocks.js b/src/mocks/mock-signature/membersMocks.js
new file mode 100644
index 0000000..89a7446
--- /dev/null
+++ b/src/mocks/mock-signature/membersMocks.js
@@ -0,0 +1,186 @@
+// Définir les rôles autorisés
+const VALID_ROLES = [
+ "User",
+ "Process",
+ "Member",
+ "Peer",
+ "Payment",
+ "Deposit",
+ "Artefact",
+ "Resolve",
+ "Backup"
+];
+
+function createProcessRole(processId, roleName) {
+ if (!VALID_ROLES.includes(roleName)) {
+ throw new Error(`Role "${roleName}" is not valid.`);
+ }
+ return { processId, role: roleName };
+}
+
+export const membersMock = [
+ {
+ id: "USR-001",
+ name: "Alice Johnson",
+ avatar: "AJ",
+ email: "alice.j@company.com",
+ processRoles: [
+ createProcessRole("PROC-001", "User"),
+ createProcessRole("PROC-002", "Process"),
+ createProcessRole("PROC-003", "Member")
+ ]
+ },
+ {
+ id: "USR-002",
+ name: "Bob Smith",
+ avatar: "BS",
+ email: "bob.s@company.com",
+ processRoles: [
+ createProcessRole("PROC-001", "Process"),
+ createProcessRole("PROC-004", "Payment")
+ ]
+ },
+ {
+ id: "USR-003",
+ name: "Charlie Davis",
+ avatar: "CD",
+ email: "charlie.d@company.com",
+ processRoles: [
+ createProcessRole("PROC-002", "Peer"),
+ createProcessRole("PROC-003", "Deposit"),
+ createProcessRole("PROC-005", "User")
+ ]
+ },
+ {
+ id: "USR-004",
+ name: "David Wilson",
+ avatar: "DW",
+ email: "david.w@company.com",
+ processRoles: [
+ createProcessRole("PROC-001", "Artefact"),
+ createProcessRole("PROC-004", "Resolve"),
+ createProcessRole("PROC-006", "Process")
+ ]
+ },
+ {
+ id: "USR-005",
+ name: "Eve Brown",
+ avatar: "EB",
+ email: "eve.b@company.com",
+ processRoles: [
+ createProcessRole("PROC-002", "Backup"),
+ createProcessRole("PROC-003", "User"),
+ createProcessRole("PROC-007", "Payment")
+ ]
+ },
+ {
+ id: "USR-006",
+ name: "Frank Miller",
+ avatar: "FM",
+ email: "frank.m@company.com",
+ processRoles: [
+ createProcessRole("PROC-001", "Member"),
+ createProcessRole("PROC-004", "Process"),
+ createProcessRole("PROC-008", "Peer")
+ ]
+ },
+ {
+ id: "USR-007",
+ name: "Grace Taylor",
+ avatar: "GT",
+ email: "grace.t@company.com",
+ processRoles: [
+ createProcessRole("PROC-005", "Deposit"),
+ createProcessRole("PROC-006", "Artefact"),
+ createProcessRole("PROC-007", "Resolve")
+ ]
+ },
+ {
+ id: "USR-008",
+ name: "Henry Clark",
+ avatar: "HC",
+ email: "henry.c@company.com",
+ processRoles: [
+ createProcessRole("PROC-002", "User"),
+ createProcessRole("PROC-005", "Backup"),
+ createProcessRole("PROC-008", "Member")
+ ]
+ },
+ {
+ id: "USR-009",
+ name: "Ivy Chen",
+ avatar: "IC",
+ email: "ivy.c@company.com",
+ processRoles: [
+ createProcessRole("PROC-003", "Process"),
+ createProcessRole("PROC-006", "Payment"),
+ createProcessRole("PROC-009", "Peer")
+ ]
+ },
+ {
+ id: "USR-010",
+ name: "Jack White",
+ avatar: "JW",
+ email: "jack.w@company.com",
+ processRoles: [
+ createProcessRole("PROC-004", "Deposit"),
+ createProcessRole("PROC-007", "Artefact"),
+ createProcessRole("PROC-010", "Resolve")
+ ]
+ },
+ {
+ id: "USR-011",
+ name: "Kelly Brown",
+ avatar: "KB",
+ email: "kelly.b@company.com",
+ processRoles: [
+ createProcessRole("PROC-001", "Backup"),
+ createProcessRole("PROC-008", "User"),
+ createProcessRole("PROC-009", "Process")
+ ]
+ },
+ {
+ id: "USR-012",
+ name: "Liam Jones",
+ avatar: "LJ",
+ email: "liam.j@company.com",
+ processRoles: [
+ createProcessRole("PROC-002", "Member"),
+ createProcessRole("PROC-009", "Payment"),
+ createProcessRole("PROC-010", "Deposit")
+ ]
+ },
+ {
+ id: "USR-013",
+ name: "Mia Wilson",
+ avatar: "MW",
+ email: "mia.w@company.com",
+ processRoles: [
+ createProcessRole("PROC-003", "Peer"),
+ createProcessRole("PROC-005", "Artefact"),
+ createProcessRole("PROC-010", "Backup")
+ ]
+ },
+ {
+ id: "USR-014",
+ name: "Noah Martin",
+ avatar: "NM",
+ email: "noah.m@company.com",
+ processRoles: [
+ createProcessRole("PROC-004", "Resolve"),
+ createProcessRole("PROC-006", "User"),
+ createProcessRole("PROC-008", "Process")
+ ]
+ },
+ {
+ id: "USR-015",
+ name: "Olivia Moore",
+ avatar: "OM",
+ email: "olivia.m@company.com",
+ processRoles: [
+ createProcessRole("PROC-005", "Member"),
+ createProcessRole("PROC-007", "Peer"),
+ createProcessRole("PROC-009", "Deposit")
+ ]
+ }
+];
diff --git a/src/mocks/mock-signature/messagesMock.ts b/src/mocks/mock-signature/messagesMock.ts
new file mode 100644
index 0000000..c00cbfd
--- /dev/null
+++ b/src/mocks/mock-signature/messagesMock.ts
@@ -0,0 +1,65 @@
+export 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" }
+ ]
+ }
+
+];
diff --git a/src/models/signature.models.ts b/src/models/signature.models.ts
new file mode 100644
index 0000000..60c4062
--- /dev/null
+++ b/src/models/signature.models.ts
@@ -0,0 +1,58 @@
+export interface Group {
+ id: number;
+ name: string;
+ description?: string;
+ roles: {
+ id?: number;
+ name: string;
+ members: { id: string | number; name: string; }[];
+ documents?: {
+ id: number;
+ name: string;
+ description?: string;
+ visibility: string;
+ createdAt: string | null;
+ deadline: string | null;
+ signatures: DocumentSignature[];
+ status?: string;
+ }[];
+ }[];
+}
+
+export interface Message {
+ id: number;
+ sender: string;
+ text?: string;
+ time: string;
+ type: 'text' | 'file';
+ fileName?: string;
+ fileData?: string;
+}
+
+export interface MemberMessages {
+ memberId: string;
+ messages: Message[];
+}
+
+export interface DocumentSignature {
+ signed: boolean;
+ member: {
+ name: string;
+ };
+ signedAt?: string;
+}
+
+export interface RequestParams {
+ processId: number;
+ processName: string;
+ roleId: number;
+ roleName: string;
+ documentId: number;
+ documentName: string;
+}
+
+export interface Notification {
+ memberId: string;
+ text: string;
+ time: string;
+}
\ No newline at end of file
diff --git a/src/pages/account/account.html b/src/pages/account/account.html
new file mode 100644
index 0000000..3b88b7f
--- /dev/null
+++ b/src/pages/account/account.html
@@ -0,0 +1,91 @@
+
+
+
+
+
+ Account
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/account/account.ts b/src/pages/account/account.ts
new file mode 100644
index 0000000..974f875
--- /dev/null
+++ b/src/pages/account/account.ts
@@ -0,0 +1,1893 @@
+import Swal from 'sweetalert2';
+
+
+//console.log("Account script loaded");
+
+let isAddingRow = false;
+let currentRow: HTMLTableRowElement | null = null;
+let currentMode: keyof typeof STORAGE_KEYS = 'pairing';
+
+interface Row {
+ column1: string;
+ column2: string;
+ column3: string;
+}
+
+// Types supplémentaires nécessaires
+interface Contract {
+ title: string;
+ date: string;
+ parties: string[];
+ terms: string[];
+ content: string;
+}
+
+interface WalletRow {
+ column1: string; // Label
+ column2: string; // Wallet
+ column3: string; // Type
+}
+
+interface DataRow {
+ column1: string; // Name
+ column2: string; // Visibility
+ column3: string; // Role
+ column4: string; // Duration
+ column5: string; // Legal
+ column6: string; // Contract
+ processName: string;
+ zone: string;
+}
+
+const ALLOWED_ROLES = ['User', 'Member', 'Peer', 'Payment', 'Deposit', 'Artefact', 'Resolve', 'Backup'];
+
+const STORAGE_KEYS = {
+ pairing: 'pairingRows',
+ wallet: 'walletRows',
+ process: 'processRows',
+ data: 'dataRows'
+};
+
+// Initialiser le stockage des lignes par défaut dans le localStorage
+const defaultRows = [
+ {
+ column1: "sprt1qqwtvg5q5vcz0reqvmld98u7va3av6gakwe9yxw9yhnpj5djcunn4squ68tuzn8dz78dg4adfv0dekx8hg9sy0t6s9k5em7rffgxmrsfpyy7gtyrz",
+ column2: "🎊😑🎄😩",
+ column3: "Laptop"
+ },
+ {
+ column1: "sprt1qqwtvg5q5vcz0reqvmld98u7va3av6gakwe9yxw9yhnpj5djcunn4squ68tuzn8dz78dg4adfv0dekx8hg9sy0t6s9k5em7rffgxmrsfpyy7gtyrx",
+ column2: "🎏🎕😧🌥",
+ column3: "Phone" }
+];
+
+// Vérifier si les lignes sont déjà dans le localStorage
+if (!localStorage.getItem('rows')) {
+ localStorage.setItem('rows', JSON.stringify(defaultRows));
+}
+
+// Add this interface
+interface Notification {
+ message: string;
+ timestamp: string;
+ isRead: boolean;
+}
+
+// Update the declaration
+const mockNotifications: { [key: string]: Notification[] } = {};
+
+const notificationMessages = [
+ "CPU usage high",
+ "Memory threshold reached",
+ "New update available",
+ "Backup completed",
+ "Security check required",
+ "Performance optimization needed",
+ "System alert",
+ "Network connectivity issue",
+ "Storage space low",
+ "Process checkpoint reached"
+];
+
+const mockDataRows = [
+ {
+ column1: "User Project",
+ column2: "private",
+ column3: "User",
+ column4: "6 months",
+ column5: "NDA signed",
+ column6: "Contract #123",
+ processName: "User Process",
+ zone: "A"
+ },
+ {
+ column1: "Process Project",
+ column2: "private",
+ column3: "Process",
+ column4: "1 year",
+ column5: "Terms accepted",
+ column6: "Contract #456",
+ processName: "Process Management",
+ zone: "B"
+ },
+ {
+ column1: "Member Project",
+ column2: "private",
+ column3: "Member",
+ column4: "3 months",
+ column5: "GDPR compliant",
+ column6: "Contract #789",
+ processName: "Member Process",
+ zone: "C"
+ },
+ {
+ column1: "Peer Project",
+ column2: "public",
+ column3: "Peer",
+ column4: "2 years",
+ column5: "IP rights",
+ column6: "Contract #101",
+ processName: "Peer Process",
+ zone: "D"
+ },
+ {
+ column1: "Payment Project",
+ column2: "confidential",
+ column3: "Payment",
+ column4: "1 year",
+ column5: "NDA signed",
+ column6: "Contract #102",
+ processName: "Payment Process",
+ zone: "E"
+ },
+ {
+ column1: "Deposit Project",
+ column2: "private",
+ column3: "Deposit",
+ column4: "6 months",
+ column5: "Terms accepted",
+ column6: "Contract #103",
+ processName: "Deposit Process",
+ zone: "F"
+ },
+ {
+ column1: "Artefact Project",
+ column2: "public",
+ column3: "Artefact",
+ column4: "1 year",
+ column5: "GDPR compliant",
+ column6: "Contract #104",
+ processName: "Artefact Process",
+ zone: "G"
+ },
+ {
+ column1: "Resolve Project",
+ column2: "private",
+ column3: "Resolve",
+ column4: "2 years",
+ column5: "IP rights",
+ column6: "Contract #105",
+ processName: "Resolve Process",
+ zone: "H"
+ },
+ {
+ column1: "Backup Project",
+ column2: "public",
+ column3: "Backup",
+ column4: "1 year",
+ column5: "NDA signed",
+ column6: "Contract #106",
+ processName: "Backup Process",
+ zone: "I"
+ }
+];
+
+const mockProcessRows = [
+ {
+ process: "User Project",
+ role: "User",
+ notification: {
+ messages: [
+ { id: 1, read: false, date: "2024-03-10", message: "New user joined the project" },
+ { id: 2, read: false, date: "2024-03-09", message: "Project milestone reached" },
+ { id: 3, read: false, date: "2024-03-08", message: "Security update required" },
+ { id: 4, read: true, date: "2024-03-07", message: "Weekly report available" },
+ { id: 5, read: true, date: "2024-03-06", message: "Team meeting scheduled" }
+ ]
+ }
+ },
+ {
+ process: "Member Project",
+ role: "Member",
+ notification: {
+ messages: [
+ { id: 6, read: true, date: "2024-03-10", message: "Member access granted" },
+ { id: 7, read: true, date: "2024-03-09", message: "Documentation updated" },
+ { id: 8, read: true, date: "2024-03-08", message: "Project status: on track" }
+ ]
+ }
+ },
+ {
+ process: "Peer Project",
+ role: "Peer",
+ notification: {
+ unread: 2,
+ total: 4,
+ messages: [
+ { id: 9, read: false, date: "2024-03-10", message: "New peer project added" },
+ { id: 10, read: false, date: "2024-03-09", message: "Project milestone reached" },
+ { id: 11, read: false, date: "2024-03-08", message: "Security update required" },
+ { id: 12, read: true, date: "2024-03-07", message: "Weekly report available" },
+ { id: 13, read: true, date: "2024-03-06", message: "Team meeting scheduled" }
+ ]
+ }
+ },
+ {
+ process: "Deposit Project",
+ role: "Deposit",
+ notification: {
+ unread: 1,
+ total: 10,
+ messages: [
+ { id: 14, read: false, date: "2024-03-10", message: "Deposit milestone reached" },
+ { id: 15, read: false, date: "2024-03-09", message: "Security update required" },
+ { id: 16, read: false, date: "2024-03-08", message: "Weekly report available" },
+ { id: 17, read: true, date: "2024-03-07", message: "Team meeting scheduled" },
+ { id: 18, read: true, date: "2024-03-06", message: "Project status: on track" }
+ ]
+ }
+ },
+ {
+ process: "Artefact Project",
+ role: "Artefact",
+ notification: {
+ unread: 0,
+ total: 3,
+ messages: [
+ { id: 19, read: false, date: "2024-03-10", message: "New artefact added" },
+ { id: 20, read: false, date: "2024-03-09", message: "Security update required" },
+ { id: 21, read: false, date: "2024-03-08", message: "Weekly report available" },
+ { id: 22, read: true, date: "2024-03-07", message: "Team meeting scheduled" },
+ { id: 23, read: true, date: "2024-03-06", message: "Project status: on track" }
+ ]
+ }
+ },
+ {
+ process: "Resolve Project",
+ role: "Resolve",
+ notification: {
+ unread: 5,
+ total: 12,
+ messages: [
+ { id: 24, read: false, date: "2024-03-10", message: "New issue reported" },
+ { id: 25, read: false, date: "2024-03-09", message: "Security update required" },
+ { id: 26, read: false, date: "2024-03-08", message: "Weekly report available" },
+ { id: 27, read: true, date: "2024-03-07", message: "Team meeting scheduled" },
+ { id: 28, read: true, date: "2024-03-06", message: "Project status: on track" }
+ ]
+ }
+ }
+];
+
+
+const mockContracts = {
+ 'Contract #123': {
+ title: "User Project Agreement",
+ date: "2024-01-15",
+ parties: ["Company XYZ", "User Team"],
+ terms: [
+ "Data Protection",
+ "User Privacy",
+ "Access Rights",
+ "Service Level Agreement"
+ ],
+ content: "This agreement establishes the terms and conditions for user data management and privacy protection..."
+ },
+ 'Contract #456': {
+ title: "Process Management Contract",
+ date: "2024-02-01",
+ parties: ["Company XYZ", "Process Team"],
+ terms: [
+ "Process Workflow",
+ "Quality Standards",
+ "Performance Metrics",
+ "Monitoring Procedures"
+ ],
+ content: "This contract defines the process management standards and operational procedures..."
+ }
+};
+
+// Fonctions de gestion des comptes et de l'interface utilisateur
+function confirmDeleteAccount(): void {
+ const modal = document.createElement('div');
+ modal.className = 'confirm-delete-modal';
+ modal.innerHTML = `
+ Delete Account
+ Are you sure you want to delete your account? This action cannot be undone.
+
+ Cancel
+ Delete
+
+ `;
+
+ document.body.appendChild(modal);
+ modal.style.display = 'block';
+
+ const cancelBtn = modal.querySelector('.cancel-btn');
+ const confirmBtn = modal.querySelector('.confirm-btn');
+
+ cancelBtn?.addEventListener('click', () => {
+ modal.remove();
+ });
+
+ confirmBtn?.addEventListener('click', () => {
+ deleteAccount();
+ modal.remove();
+ });
+}
+
+function deleteAccount(): void {
+ localStorage.clear();
+ window.location.href = '/login.html';
+}
+
+function updateNavbarBanner(imageUrl: string): void {
+ const navbarSection = document.querySelector('.nav-wrapper .avatar-section');
+ if (!navbarSection) return;
+
+ let bannerImg = navbarSection.querySelector('.banner-image');
+
+ if (!bannerImg) {
+ bannerImg = document.createElement('img');
+ bannerImg.className = 'banner-image';
+ navbarSection.insertBefore(bannerImg, navbarSection.firstChild);
+ }
+
+ bannerImg.src = imageUrl;
+}
+
+function saveBannerToLocalStorage(dataUrl: string): void {
+ localStorage.setItem('userBanner', dataUrl);
+}
+
+function loadSavedBanner(): void {
+ const savedBanner = localStorage.getItem('userBanner');
+ if (savedBanner) {
+ const bannerImg = document.getElementById('popup-banner-img') as HTMLImageElement;
+ if (bannerImg) {
+ bannerImg.src = savedBanner;
+ }
+ updateNavbarBanner(savedBanner);
+ }
+}
+
+
+function closeNotificationPopup(event: Event): void {
+ const target = event.target as HTMLElement;
+ const isOverlay = target.classList.contains('notification-popup-overlay');
+ const isCloseButton = target.classList.contains('close-popup');
+ if (!isOverlay && !isCloseButton) return;
+
+ const popup = document.querySelector('.notification-popup-overlay');
+ if (popup) popup.remove();
+}
+
+function markAsRead(processName: string, messageId: number, element: HTMLElement): void {
+ const process = mockProcessRows.find(p => p.process === processName);
+ if (!process) return;
+
+ const message = process.notification.messages.find(m => m.id === messageId);
+ if (!message || message.read) return;
+
+ message.read = true;
+
+ element.classList.remove('unread');
+ element.classList.add('read');
+ const statusIcon = element.querySelector('.notification-status i');
+ if (statusIcon) {
+ statusIcon.classList.remove('fa-circle');
+ statusIcon.classList.add('fa-check');
+ }
+
+ const notifCount = calculateNotifications(process.notification.messages);
+ const countElement = document.querySelector(`.notification-count[data-process="${processName}"]`);
+ if (countElement) {
+ countElement.textContent = `${notifCount.unread}/${notifCount.total}`;
+
+ const bellContainer = countElement.closest('.notification-container');
+ const bell = bellContainer?.querySelector('.fa-bell');
+ if (bell && bellContainer && notifCount.unread === 0) {
+ bellContainer.classList.remove('has-unread');
+ (bell as HTMLElement).style.color = '#666';
+ }
+ }
+}
+
+// Fonctions de gestion des données et de l'interface
+interface NotificationMessage {
+ id: number;
+ read: boolean;
+ date: string;
+ message: string;
+}
+
+function calculateNotifications(messages: NotificationMessage[]): { unread: number; total: number } {
+ const total = messages.length;
+ const unread = messages.filter(msg => !msg.read).length;
+ return { unread, total };
+}
+
+// Fonctions de récupération
+function exportRecovery(): void {
+ Swal.fire({
+ title: 'Recovery Words Export',
+ text: '4 words will be displayed. We strongly recommend writing them down on paper before exporting the account. Do you want to continue?',
+ icon: 'warning',
+ showCancelButton: true,
+ confirmButtonText: 'Confirm',
+ cancelButtonText: 'Cancel',
+ confirmButtonColor: '#C89666',
+ cancelButtonColor: '#6c757d'
+ }).then((result) => {
+ if (result.isConfirmed) {
+ const recoveryWords = generateRecoveryWords();
+ localStorage.setItem('recoveryWords', JSON.stringify(recoveryWords));
+
+ Swal.fire({
+ title: 'Your Recovery Words',
+ html: `
+
+ ${recoveryWords.map((word, index) => `
+
+ ${index + 1}.
+ ${word}
+
+ `).join('')}
+
+
+ Please write these words down carefully. They will be needed to recover your account.
+
+ `,
+ showCancelButton: false,
+ confirmButtonText: 'I confirm the export',
+ confirmButtonColor: '#C89666',
+ allowOutsideClick: false,
+ allowEscapeKey: false
+ }).then((result) => {
+ if (result.isConfirmed) {
+ // Stocker l'état du bouton dans le localStorage
+ localStorage.setItem('recoveryExported', 'true');
+
+ const exportRecoveryBtn = document.querySelector('.recovery-btn') as HTMLButtonElement;
+ if (exportRecoveryBtn) {
+ exportRecoveryBtn.disabled = true;
+ exportRecoveryBtn.style.opacity = '0.5';
+ exportRecoveryBtn.style.cursor = 'not-allowed';
+ }
+ }
+ });
+ }
+ });
+}
+
+const wordsList: string[] = [
+ "apple", "banana", "carrot", "diamond", "elephant", "forest", "guitar", "hammer",
+ "island", "jacket", "kettle", "lemon", "marble", "needle", "orange", "pencil",
+ "quartz", "rabbit", "sunset", "turtle", "umbrella", "violet", "wallet", "yellow"
+];
+
+function generateRecoveryWords(): string[] {
+ const recoveryWords: string[] = [];
+ while (recoveryWords.length < 4) {
+ const randomWord = wordsList[Math.floor(Math.random() * wordsList.length)];
+ if (!recoveryWords.includes(randomWord)) {
+ recoveryWords.push(randomWord);
+ }
+ }
+ return recoveryWords;
+}
+
+function exportUserData(): void {
+ const data: { [key: string]: string | null } = {};
+ for (let i = 0; i < localStorage.length; i++) {
+ const key = localStorage.key(i);
+ if (key) {
+ const value = localStorage.getItem(key);
+ data[key] = value;
+ }
+ }
+
+ const jsonData = JSON.stringify(data, null, 2);
+ const blob = new Blob([jsonData], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'user_data.json';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+}
+
+function updateActionButtons(): void {
+ const buttonContainer = document.querySelector('.button-container');
+ if (!buttonContainer) return;
+
+ buttonContainer.innerHTML = `
+
+ ✓
+ ✗
+
+ `;
+}
+
+function getConfirmFunction(): string {
+ switch (currentMode) {
+ case 'wallet':
+ return 'window.confirmWalletRow()';
+ case 'process':
+ return 'window.confirmProcessRow()';
+ default:
+ return 'window.confirmRow()';
+ }
+}
+
+function getCancelFunction(): string {
+ switch (currentMode) {
+ case 'wallet':
+ return 'window.cancelWalletRow()';
+ case 'process':
+ return 'window.cancelProcessRow()';
+ default:
+ return 'window.cancelRow()';
+ }
+}
+
+// Fonctions de gestion des tableaux
+function addRow(): void {
+ if (isAddingRow) return;
+
+ isAddingRow = true;
+ const table = document.querySelector('#pairing-table tbody');
+ if (!table) return;
+
+ currentRow = table.insertRow();
+ const cells = ['SP Address', 'Device Name', 'SP Emojis'];
+
+ cells.forEach((_, index) => {
+ const cell = currentRow!.insertCell();
+ const input = document.createElement('input');
+ input.type = 'text';
+ input.className = 'edit-input';
+
+ // Ajouter un événement pour mettre à jour automatiquement les emojis
+ if (index === 0) { // SP Address input
+ input.addEventListener('input', (e) => {
+ const addressInput = e.target as HTMLInputElement;
+ const emojiCell = currentRow!.cells[2]; // Changé à 2 pour la troisième colonne
+ const emojis = addressToEmoji(addressInput.value);
+ if (emojiCell.querySelector('input')) {
+ (emojiCell.querySelector('input') as HTMLInputElement).value = emojis;
+ }
+ });
+ }
+
+ // Désactiver l'input des emojis car il est automatique
+ if (index === 2) { // SP Emojis input (troisième colonne)
+ input.readOnly = true;
+ }
+
+ cell.appendChild(input);
+ });
+
+ const deleteCell = currentRow.insertCell();
+ deleteCell.style.width = '40px';
+
+ updateActionButtons();
+}
+
+
+
+function confirmRow(): void {
+ if (!currentRow) return;
+
+ const inputs = currentRow.getElementsByTagName('input');
+ const values: string[] = Array.from(inputs).map(input => input.value.trim());
+
+ // Vérification des champs vides
+ if (values.some(value => value === '')) {
+ showAlert('Please fill in all fields');
+ return;
+ }
+
+ // Vérification de la longueur de l'adresse SP
+ if (values[0].length !== 118) {
+ showAlert('SP Address must be exactly 118 characters long');
+ return;
+ }
+
+ const newRow: Row = {
+ column1: values[0],
+ column2: values[1],
+ column3: values[2]
+ };
+
+ const storageKey = STORAGE_KEYS[currentMode];
+ const rows: Row[] = JSON.parse(localStorage.getItem(storageKey) || '[]');
+ rows.push(newRow);
+ localStorage.setItem(storageKey, JSON.stringify(rows));
+
+ isAddingRow = false;
+ currentRow = null;
+
+ resetButtonContainer();
+ updateTableContent(rows);
+}
+
+function cancelRow(): void {
+ if (!currentRow) return;
+
+ currentRow.remove();
+ isAddingRow = false;
+ currentRow = null;
+
+ resetButtonContainer();
+}
+
+function resetButtonContainer(): void {
+ const buttonContainer = document.querySelector('.button-container');
+ if (!buttonContainer) return;
+
+ buttonContainer.innerHTML = `
+ Add a line
+ `;
+}
+
+function deleteRow(button: HTMLButtonElement): void {
+ const row = button.closest('tr');
+ if (!row) return;
+
+ const table = row.closest('tbody');
+ if (!table) return;
+
+ // Vérifier le nombre de lignes restantes
+ const remainingRows = table.getElementsByTagName('tr').length;
+ if (remainingRows <= 2) {
+ showAlert('You must keep at least 2 devices paired');
+ return;
+ }
+
+ // Animation de suppression
+ row.style.transition = 'opacity 0.3s';
+ row.style.opacity = '0';
+
+ setTimeout(() => {
+ // Obtenir l'index avant la suppression
+ const index = Array.from(table.children).indexOf(row);
+
+ // Supprimer la ligne du DOM
+ row.remove();
+
+ // Mettre à jour le localStorage
+ const storageKey = STORAGE_KEYS[currentMode];
+ const rows = JSON.parse(localStorage.getItem(storageKey) || '[]');
+ rows.splice(index, 1);
+ localStorage.setItem(storageKey, JSON.stringify(rows));
+ }, 300);
+}
+
+function editDeviceName(cell: HTMLTableCellElement): void {
+ if (cell.classList.contains('editing')) return;
+
+ const currentValue = cell.textContent || '';
+ const input = document.createElement('input');
+ input.type = 'text';
+ input.value = currentValue;
+ input.className = 'edit-input';
+
+ input.addEventListener('blur', () => finishEditing(cell, input));
+ input.addEventListener('keypress', (e: KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ finishEditing(cell, input);
+ }
+ });
+
+ cell.textContent = '';
+ cell.appendChild(input);
+ cell.classList.add('editing');
+ input.focus();
+}
+
+function finishEditing(cell: HTMLTableCellElement, input: HTMLInputElement): void {
+ const newValue = input.value.trim();
+ if (newValue === '') {
+ cell.textContent = cell.getAttribute('data-original-value') || '';
+ cell.classList.remove('editing');
+ return;
+ }
+
+ const row = cell.closest('tr');
+ if (!row) return;
+
+ const table = row.closest('tbody');
+ if (!table) return;
+
+ const index = Array.from(table.children).indexOf(row);
+ const storageKey = STORAGE_KEYS[currentMode];
+ const rows: Row[] = JSON.parse(localStorage.getItem(storageKey) || '[]');
+
+ if (rows[index]) {
+ rows[index].column3 = newValue;
+ localStorage.setItem(storageKey, JSON.stringify(rows));
+ }
+
+ cell.textContent = newValue;
+ cell.classList.remove('editing');
+}
+
+function showAlert(message: string): void {
+ // Créer la popup si elle n'existe pas
+ let alertPopup = document.querySelector('.alert-popup');
+ if (!alertPopup) {
+ alertPopup = document.createElement('div');
+ alertPopup.className = 'alert-popup';
+ document.body.appendChild(alertPopup);
+ }
+
+ // Définir le message et afficher la popup
+ alertPopup.textContent = message;
+ (alertPopup as HTMLElement).style.display = 'block';
+
+ // Cacher la popup après 3 secondes
+ setTimeout(() => {
+ (alertPopup as HTMLElement).style.display = 'none';
+ }, 3000);
+}
+
+// Fonction pour gérer les événements d'édition
+function initializeEventListeners(): void {
+ const editableFields = document.querySelectorAll('.editable');
+ editableFields.forEach(field => {
+ field.addEventListener('click', () => {
+ if (!field.classList.contains('editing')) {
+ const currentValue = field.textContent || '';
+ const input = document.createElement('input');
+ input.type = 'text';
+ input.value = currentValue;
+ input.className = 'edit-input';
+
+ field.textContent = '';
+ field.appendChild(input);
+ field.classList.add('editing');
+ input.focus();
+ }
+ });
+ });
+
+ const avatarInput = document.getElementById('avatar-upload') as HTMLInputElement;
+ if (avatarInput) {
+ avatarInput.addEventListener('change', handleAvatarUpload);
+ }
+}
+
+// Fonction pour gérer le téléchargement de l'avatar
+function handleAvatarUpload(event: Event): void {
+ const input = event.target as HTMLInputElement;
+ const file = input.files?.[0];
+
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = (e: ProgressEvent) => {
+ const result = e.target?.result as string;
+ const popupAvatar = document.getElementById('popup-avatar-img') as HTMLImageElement;
+ const navAvatar = document.querySelector('.nav-wrapper .avatar') as HTMLImageElement;
+
+ if (popupAvatar) popupAvatar.src = result;
+ if (navAvatar) navAvatar.src = result;
+
+ localStorage.setItem('userAvatar', result);
+ };
+ reader.readAsDataURL(file);
+ }
+}
+
+// Initialisation au chargement de la page
+if (!localStorage.getItem('rows')) {
+ localStorage.setItem('rows', JSON.stringify(defaultRows));
+}
+
+// Event Listeners et initialisation
+document.addEventListener('DOMContentLoaded', () => {
+ //console.log("DOM Content Loaded");
+ initAccount();
+ showPairing();
+});
+
+
+// Ajout des fonctions manquantes
+
+
+function showProcess(): void {
+ //console.log("showProcess called");
+ currentMode = 'process';
+ hideAllContent();
+
+ const headerTitle = document.getElementById('header-title');
+ if (headerTitle) headerTitle.textContent = 'Process';
+
+ const processContent = document.getElementById('process-content');
+ if (processContent) {
+ processContent.style.display = 'block';
+ processContent.innerHTML = `
+
+
+
+
+
+ Process Name
+ Role
+ Notifications
+
+
+
+
+
+ `;
+
+ // Utiliser mockProcessRows au lieu du localStorage
+ updateProcessTableContent(mockProcessRows);
+ }
+}
+
+// Fonction utilitaire pour mettre à jour le contenu du tableau Process
+function updateProcessTableContent(rows: any[]): void {
+ const tbody = document.querySelector('#process-table tbody');
+ if (!tbody) return;
+
+ tbody.innerHTML = rows.map(row => `
+
+ ${row.process}
+ ${row.role}
+
+
+
+
+ ${row.notification?.messages?.filter((m: any) => !m.read).length || 0}/${row.notification?.messages?.length || 0}
+
+
+
+
+ `).join('');
+}
+
+
+
+function showProcessNotifications(processName: string): void {
+ const process = mockProcessRows.find(p => p.process === processName);
+ if (!process) return;
+
+ const modal = document.createElement('div');
+ modal.className = 'notifications-modal';
+
+ let notificationsList = process.notification.messages.map(msg => `
+
+
+
+
+
+ ${msg.message}
+ ${msg.date}
+
+
+ `).join('');
+
+ if (process.notification.messages.length === 0) {
+ notificationsList = 'No notifications
';
+ }
+
+ modal.innerHTML = `
+
+
${processName} Notifications
+
+ ${notificationsList}
+
+
x
+
+ `;
+
+ document.body.appendChild(modal);
+
+ // Mettre à jour le compteur de notifications
+ const countElement = document.querySelector(`.notification-count[data-process="${processName}"]`);
+ if (countElement) {
+ const notifCount = calculateNotifications(process.notification.messages);
+ countElement.textContent = `${notifCount.unread}/${notifCount.total}`;
+ }
+
+ const closeButton = modal.querySelector('.close-notifications');
+ closeButton?.addEventListener('click', () => {
+ modal.remove();
+ showProcess(); // Rafraîchir l'affichage pour mettre à jour les compteurs
+ });
+}
+
+function generateRandomNotification(): void {
+ const processes = JSON.parse(localStorage.getItem(STORAGE_KEYS.process) || '[]');
+ if (processes.length === 0) return;
+
+ const randomProcess = processes[Math.floor(Math.random() * processes.length)].column1;
+ const notification = {
+ message: notificationMessages[Math.floor(Math.random() * notificationMessages.length)],
+ timestamp: new Date().toLocaleString(),
+ isRead: false
+ };
+
+ if (!mockNotifications[randomProcess]) {
+ mockNotifications[randomProcess] = [];
+ }
+ mockNotifications[randomProcess].push(notification);
+
+ if (currentMode === 'process') {
+ showProcess();
+ }
+}
+
+function handleLogout(): void {
+ localStorage.clear();
+ window.location.href = '../login/login.html';
+}
+
+
+
+// Initialisation des notifications
+document.addEventListener('DOMContentLoaded', () => {
+ loadUserInfo();
+ setInterval(generateRandomNotification, 60000);
+});
+
+
+// Fonctions de gestion des contrats
+function showContractPopup(contractId: string): void {
+ const contract = mockContracts[contractId as keyof typeof mockContracts];
+ if (!contract) {
+ showAlert('Contract not found');
+ return;
+ }
+
+ const popupHTML = `
+
+ `;
+
+ document.body.insertAdjacentHTML('beforeend', popupHTML);
+}
+
+// Fonction utilitaire pour cacher tous les contenus
+function hideAllContent(): void {
+ const contents = ['pairing-content', 'wallet-content', 'process-content', 'data-content'];
+ contents.forEach(id => {
+ const element = document.getElementById(id);
+ if (element) {
+ element.style.display = 'none';
+ }
+ });
+}
+
+// Fonctions d'affichage des sections
+function showPairing(): void {
+
+ isAddingRow = false;
+ currentRow = null;
+
+ currentMode = 'pairing';
+ // Cacher tous les contenus
+ hideAllContent();
+
+ // Mettre à jour le titre
+ const headerElement = document.getElementById('parameter-header');
+ if (headerElement) {
+ headerElement.textContent = 'Pairing';
+ }
+
+
+ // Afficher le contenu de pairing
+ const pairingContent = document.getElementById('pairing-content');
+
+ if (pairingContent) {
+ pairingContent.style.display = 'block';
+ pairingContent.innerHTML = `
+
+
+
+
+
+ SP Address
+ Device Name
+ SP Emojis
+
+
+
+
+
+
+ Add a line
+
+
+ `;
+
+ // Mettre à jour le contenu du tableau
+ const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.pairing) || '[]');
+ updateTableContent(rows);
+ }
+}
+
+function showWallet(): void {
+ isAddingRow = false;
+ currentRow = null;
+
+ currentMode = 'wallet';
+ hideAllContent();
+
+ // Mettre à jour le titre
+ const headerTitle = document.getElementById('header-title');
+ if (headerTitle) headerTitle.textContent = 'Wallet';
+
+ const walletContent = document.getElementById('wallet-content');
+ if (!walletContent) return;
+ walletContent.style.display = 'block'; // Ajout de cette ligne pour rendre visible
+ walletContent.innerHTML = `
+
+
+
+
+
+ Label
+ Wallet
+ Type
+
+
+
+
+
+ Add a line
+
+
+ `;
+
+ const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || '[]');
+ updateWalletTableContent(rows);
+}
+
+
+function updateWalletTableContent(rows: WalletRow[]): void {
+ const tbody = document.querySelector('#wallet-table tbody');
+ if (!tbody) return;
+
+ tbody.innerHTML = rows.map(row => `
+
+ ${row.column1}
+ ${row.column2}
+ ${row.column3}
+
+ `).join('');
+}
+
+function showData(): void {
+ //console.log("showData called");
+ currentMode = 'data';
+ hideAllContent();
+
+ const headerTitle = document.getElementById('header-title');
+ if (headerTitle) headerTitle.textContent = 'Data';
+
+ const dataContent = document.getElementById('data-content');
+ if (dataContent) {
+ dataContent.style.display = 'block';
+ dataContent.innerHTML = `
+
+
+
+
+
+ Name
+ Visibility
+ Role
+ Duration
+ Legal
+ Contract
+
+
+
+
+
+ `;
+
+ const rows = mockDataRows || JSON.parse(localStorage.getItem(STORAGE_KEYS.data) || '[]');
+ updateDataTableContent(rows);
+ }
+}
+
+// Fonctions de gestion du wallet
+function addWalletRow(): void {
+ if (isAddingRow) return;
+ isAddingRow = true;
+
+ const table = document.getElementById('wallet-table')?.getElementsByTagName('tbody')[0];
+ if (!table) return;
+
+ currentRow = table.insertRow();
+ const placeholders = ['Label', 'Wallet', 'Type'];
+
+ placeholders.forEach(placeholder => {
+ const cell = currentRow!.insertCell();
+ const input = document.createElement('input');
+ input.type = 'text';
+ input.placeholder = placeholder;
+ input.className = 'edit-input';
+ cell.appendChild(input);
+ });
+
+ // Remplacer le bouton "Add a line" par les boutons de confirmation/annulation
+ const buttonContainer = document.querySelector('#wallet-content .button-container');
+ if (!buttonContainer) return;
+
+ buttonContainer.innerHTML = `
+
+ ✓
+ ✗
+
+ `;
+
+ updateActionButtons();
+}
+
+function confirmWalletRow(): void {
+ if (!currentRow) return;
+
+ const inputs = Array.from(currentRow.getElementsByTagName('input'));
+ const allFieldsFilled = inputs.every(input => input.value.trim() !== '');
+
+ if (allFieldsFilled) {
+ const newRow: WalletRow = {
+ column1: inputs[0].value.trim(),
+ column2: inputs[1].value.trim(),
+ column3: inputs[2].value.trim()
+ };
+
+ const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || '[]');
+ rows.push(newRow);
+ localStorage.setItem(STORAGE_KEYS.wallet, JSON.stringify(rows));
+
+ isAddingRow = false;
+ currentRow = null;
+ showWallet();
+ } else {
+ showAlert('Please complete all fields before confirming.');
+ }
+}
+
+function cancelWalletRow(): void {
+ if (!currentRow) return;
+
+ currentRow.remove();
+ isAddingRow = false;
+ currentRow = null;
+
+ // Réinitialiser le conteneur de boutons avec le bouton "Add a line"
+ const buttonContainer = document.querySelector('#wallet-content .button-container');
+ if (!buttonContainer) return;
+
+ buttonContainer.innerHTML = `
+ Add a line
+ `;
+
+
+}
+
+// Fonctions de sauvegarde
+function saveDataRowToLocalStorage(row: DataRow): void {
+ const rows: DataRow[] = JSON.parse(localStorage.getItem(STORAGE_KEYS.data) || '[]');
+ rows.push(row);
+ localStorage.setItem(STORAGE_KEYS.data, JSON.stringify(rows));
+}
+
+function saveDeviceName(cell: HTMLTableCellElement, newValue: string): void {
+ const row = cell.closest('tr');
+ if (!row) return;
+
+ const index = Array.from(row.parentElement?.children || []).indexOf(row);
+ const rows: Row[] = JSON.parse(localStorage.getItem(STORAGE_KEYS[currentMode]) || '[]');
+
+ if (rows[index]) {
+ rows[index].column3 = newValue;
+ localStorage.setItem(STORAGE_KEYS[currentMode], JSON.stringify(rows));
+ }
+}
+
+function saveRowToLocalStorage(row: Row): void {
+ const rows: Row[] = JSON.parse(localStorage.getItem(STORAGE_KEYS[currentMode]) || '[]');
+ rows.push(row);
+ localStorage.setItem(STORAGE_KEYS[currentMode], JSON.stringify(rows));
+}
+
+
+// Fonctions de mise à jour de l'interface
+function updateTableContent(rows: Row[]): void {
+ const tbody = document.querySelector('#pairing-table tbody');
+ if (!tbody) return;
+
+ tbody.innerHTML = rows.map(row => `
+
+ ${row.column1}
+ ${row.column2}
+ ${row.column3}
+
+
+
+
+
+
+ `).join('');
+}
+
+function updateDataTableContent(rows: DataRow[]): void {
+ const tbody = document.querySelector('#data-table tbody');
+ if (!tbody) return;
+
+ tbody.innerHTML = rows.map(row => `
+
+ ${row.column1}
+ ${row.column2}
+ ${row.column3}
+ ${row.column4}
+ ${row.column5}
+
+ ${row.column6}
+
+
+ `).join('');
+}
+
+// Fonctions de gestion de l'avatar et de la bannière
+function openAvatarPopup(): void {
+ const popup = document.getElementById('avatar-popup');
+ if (!popup) return;
+
+ // Récupérer les valeurs stockées
+ const savedName = localStorage.getItem('userName') || 'Luke';
+ const savedLastName = localStorage.getItem('userLastName') || 'Doe';
+ const savedAvatar = localStorage.getItem('userAvatar') || 'https://via.placeholder.com/150';
+ const savedBanner = localStorage.getItem('userBanner') || 'https://via.placeholder.com/800x200';
+ const savedAddress = localStorage.getItem('userAddress') || '🏠 🌍 🗽🎊😩-🎊😑🎄😩';
+
+ popup.innerHTML = `
+
+ `;
+
+ popup.style.display = 'block';
+ setupEventListeners(popup);
+
+ // Ajouter le gestionnaire d'événements pour la bannière
+ const bannerImg = popup.querySelector('#popup-banner-img');
+ const bannerInput = popup.querySelector('#banner-upload') as HTMLInputElement;
+
+ if (bannerImg && bannerInput) {
+ bannerImg.addEventListener('click', () => {
+ bannerInput.click();
+ });
+ }
+
+ // Après avoir créé le popup, vérifier si recovery a déjà été exporté
+ const recoveryExported = localStorage.getItem('recoveryExported') === 'true';
+ if (recoveryExported) {
+ const exportRecoveryBtn = popup.querySelector('.recovery-btn') as HTMLButtonElement;
+ if (exportRecoveryBtn) {
+ exportRecoveryBtn.disabled = true;
+ exportRecoveryBtn.style.opacity = '0.5';
+ exportRecoveryBtn.style.cursor = 'not-allowed';
+ }
+ }
+}
+
+function setupEventListeners(popup: HTMLElement): void {
+ // Gestionnaire pour la fermeture
+ const closeBtn = popup.querySelector('.close-popup');
+ if (closeBtn) {
+ closeBtn.addEventListener('click', () => {
+ popup.style.display = 'none';
+ });
+ }
+
+ // Gestionnaire pour l'upload d'avatar
+ const avatarUpload = popup.querySelector('#avatar-upload') as HTMLInputElement;
+ if (avatarUpload) {
+ avatarUpload.addEventListener('change', (e: Event) => {
+ const file = (e.target as HTMLInputElement).files?.[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = (e: ProgressEvent) => {
+ const result = e.target?.result as string;
+ const popupAvatar = document.getElementById('popup-avatar-img') as HTMLImageElement;
+ const navAvatar = document.querySelector('.nav-wrapper .avatar') as HTMLImageElement;
+
+ if (popupAvatar) popupAvatar.src = result;
+ if (navAvatar) navAvatar.src = result;
+ localStorage.setItem('userAvatar', result);
+ };
+ reader.readAsDataURL(file);
+ }
+ });
+ }
+
+ // Gestionnaire pour l'upload de bannière
+ const bannerUpload = popup.querySelector('#banner-upload') as HTMLInputElement;
+ if (bannerUpload) {
+ bannerUpload.addEventListener('change', (e: Event) => {
+ const file = (e.target as HTMLInputElement).files?.[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = (e: ProgressEvent) => {
+ const result = e.target?.result as string;
+ const popupBanner = document.getElementById('popup-banner-img') as HTMLImageElement;
+ if (popupBanner) popupBanner.src = result;
+ updateNavbarBanner(result);
+ localStorage.setItem('userBanner', result);
+ };
+ reader.readAsDataURL(file);
+ }
+ });
+ }
+
+ // Gestionnaires pour les champs de texte
+ const nameInput = popup.querySelector('#userName') as HTMLInputElement;
+ const lastNameInput = popup.querySelector('#userLastName') as HTMLInputElement;
+
+ if (nameInput) {
+ nameInput.addEventListener('input', () => {
+ localStorage.setItem('userName', nameInput.value);
+ const userNameElement = document.querySelector('.user-name');
+ if (userNameElement) {
+ userNameElement.textContent = nameInput.value;
+ }
+ });
+ }
+
+ if (lastNameInput) {
+ lastNameInput.addEventListener('input', () => {
+ const newLastName = lastNameInput.value;
+ localStorage.setItem('userLastName', newLastName);
+ updateNavbarLastName(newLastName);
+ });
+ }
+}
+
+function closeAvatarPopup(): void {
+ const popup = document.querySelector('.avatar-popup');
+ if (popup) popup.remove();
+}
+
+function saveUserInfo(): void {
+ const nameInput = document.querySelector('#user-name-input');
+ if (nameInput) {
+ localStorage.setItem('userName', nameInput.value);
+ }
+}
+
+function saveAvatarToLocalStorage(dataUrl: string): void {
+ localStorage.setItem('userAvatar', dataUrl);
+}
+
+function loadAvatar(): void {
+ const savedAvatar = localStorage.getItem('userAvatar');
+ if (savedAvatar) {
+ const avatarImg = document.querySelector('.avatar') as HTMLImageElement;
+ if (avatarImg) {
+ avatarImg.src = savedAvatar;
+ }
+ }
+}
+
+function loadUserInfo(): void {
+ const savedName = localStorage.getItem('userName');
+ const savedLastName = localStorage.getItem('userLastName');
+
+ if (savedName) {
+ const nameDisplay = document.querySelector('.user-name');
+ if (nameDisplay) {
+ nameDisplay.textContent = savedName;
+ }
+ }
+
+ if (savedLastName) {
+ const lastNameDisplay = document.querySelector('.user-lastname');
+ if (lastNameDisplay) {
+ lastNameDisplay.textContent = savedLastName;
+ }
+ }
+
+ loadAvatar();
+ loadSavedBanner();
+}
+
+
+function addressToEmoji(address: string): string {
+ // Utiliser l'adresse pour générer un hash déterministe
+ let hash = 0;
+ for (let i = 0; i < address.length; i++) {
+ hash = ((hash << 5) - hash) + address.charCodeAt(i);
+ hash = hash & hash;
+ }
+
+ // Utiliser le hash pour sélectionner les emojis
+ const emojis = generateEmojiList();
+ const selectedEmojis: string[] = [];
+
+ // Sélectionner 4 emojis basés sur le hash de l'adresse
+ for (let i = 0; i < 4; i++) {
+ const index = Math.abs(hash + i) % emojis.length;
+ selectedEmojis.push(emojis[index]);
+ }
+
+ return selectedEmojis.join('');
+}
+
+function isValidRole(role: string): boolean {
+ const validRoles = ['admin', 'user', 'guest', 'manager'];
+ return validRoles.includes(role.toLowerCase());
+}
+
+// Fonction pour annuler l'ajout d'une ligne
+function cancelAddRow(): void {
+ if (!currentRow) return;
+ currentRow.remove();
+ isAddingRow = false;
+ currentRow = null;
+
+ const buttonContainer = document.querySelector('.button-container');
+ if (buttonContainer) {
+ buttonContainer.innerHTML = `
+ Add a line
+ `;
+ }
+}
+
+// Fonction pour sauvegarder le nom
+function saveName(cell: HTMLElement, input: HTMLInputElement): void {
+ const newValue = input.value.trim();
+ const originalValue = cell.getAttribute('data-original-value');
+
+ // Si vide, revenir à la valeur originale
+ if (newValue === '') {
+ cell.textContent = originalValue;
+ return;
+ }
+
+ // Mettre à jour l'affichage
+ cell.textContent = newValue;
+
+ // Mettre à jour le localStorage
+ const storageKey = STORAGE_KEYS.data;
+ const rows = JSON.parse(localStorage.getItem(storageKey) || '[]');
+
+ const updatedRows = rows.map((row: any) => {
+ if (row.column1 === originalValue) {
+ return { ...row, column1: newValue };
+ }
+ return row;
+ });
+
+ localStorage.setItem(storageKey, JSON.stringify(updatedRows));
+}
+
+// Fonction pour mettre à jour le localStorage après suppression
+function updateLocalStorageAfterDelete(index: number): void {
+ const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS[currentMode]) || '[]');
+ rows.splice(index, 1);
+ localStorage.setItem(STORAGE_KEYS[currentMode], JSON.stringify(rows));
+}
+
+// Fonction pour désactiver les boutons de suppression
+function disableDeleteButtons(disabled: boolean): void {
+ const deleteButtons = document.querySelectorAll('.delete-button');
+ deleteButtons.forEach(button => {
+ button.disabled = disabled;
+ button.style.opacity = disabled ? '0.5' : '1';
+ button.style.cursor = disabled ? 'not-allowed' : 'pointer';
+ });
+}
+
+// Fonction pour mettre à jour le contenu de la bannière
+function updateBannerContent(): void {
+ const bannerImg = document.getElementById('popup-banner-img') as HTMLImageElement;
+ const bannerUpload = document.getElementById('banner-upload') as HTMLInputElement;
+
+ if (bannerUpload && bannerImg) {
+ bannerUpload.addEventListener('change', (e: Event) => {
+ const file = (e.target as HTMLInputElement).files?.[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = (e: ProgressEvent) => {
+ const result = e.target?.result as string;
+ bannerImg.src = result;
+ saveBannerToLocalStorage(result);
+ updateNavbarBanner(result);
+ };
+ reader.readAsDataURL(file);
+ }
+ });
+ }
+}
+
+// Fonction pour générer la liste d'emojis
+function generateEmojiList(): string[] {
+ const emojis = ['😀', '😃', '😄', '😁', '😅', '😂', '🤣', '😊', '😇', '🙂', '🙃', '😉'];
+ const result: string[] = [];
+
+ while (result.length < 4) {
+ const emoji = emojis[Math.floor(Math.random() * emojis.length)];
+ if (!result.includes(emoji)) {
+ result.push(emoji);
+ }
+ }
+
+ return result;
+}
+
+// Fonction pour afficher les notifications
+function showNotifications(processName: string): void {
+ const process = mockProcessRows.find(p => p.process === processName);
+ if (!process) return;
+
+ const popupHTML = `
+
+
+
×
+
${processName} Notifications
+
+ ${process.notification.messages.map(msg => `
+
+
+ ${msg.read ?
+ '✓ ' :
+ '● '
+ }
+
+
+
${msg.message}
+
${msg.date}
+
+
+ `).join('')}
+
+
+
+ `;
+
+ document.body.insertAdjacentHTML('beforeend', popupHTML);
+
+ // Event listeners
+ const modal = document.querySelector('.notifications-modal');
+ const closeBtn = document.querySelector('.close-button');
+
+ if (modal && closeBtn) {
+ modal.addEventListener('click', (e) => {
+ if (e.target === modal) {
+ modal.remove();
+ }
+ });
+
+ closeBtn.addEventListener('click', () => {
+ modal.remove();
+ });
+ }
+}
+
+function updateNavbarName(name: string): void {
+ const nameElement = document.querySelector('.nav-wrapper .user-name');
+ if (nameElement) {
+ nameElement.textContent = name;
+ }
+}
+
+function updateNavbarLastName(lastName: string): void {
+ const lastNameElement = document.querySelector('.user-lastname');
+ if (lastNameElement) {
+ lastNameElement.textContent = lastName;
+ }
+}
+
+// Ajout des déclarations globales
+declare global {
+ interface Window {
+ initAccount: () => void;
+ showContractPopup: (contractId: string) => void;
+ showPairing: () => void;
+ showWallet: () => void;
+ showData: () => void;
+ addWalletRow: () => void;
+ confirmWalletRow: () => void;
+ cancelWalletRow: () => void;
+ openAvatarPopup: () => void;
+ closeAvatarPopup: () => void;
+ editDeviceName: (cell: HTMLTableCellElement) => void;
+ showNotifications: (processName: string) => void;
+ closeNotificationPopup: (event: Event) => void;
+ markAsRead: (processName: string, messageId: number, element: HTMLElement) => void;
+ exportRecovery: () => void;
+ confirmDeleteAccount: () => void;
+ deleteAccount: () => void;
+ updateNavbarBanner: (bannerUrl: string) => void;
+ saveBannerToLocalStorage: (bannerUrl: string) => void;
+ loadSavedBanner: () => void;
+ cancelAddRow: () => void;
+ saveName: (cell: HTMLElement, input: HTMLInputElement) => void;
+ showProcessNotifications: (processName: string) => void;
+ handleLogout: () => void;
+ initializeEventListeners: () => void;
+ showProcess: () => void;
+ updateNavbarName: (name: string) => void;
+ updateNavbarLastName: (lastName: string) => void;
+ }
+}
+
+// Assignation des fonctions à l'objet window
+Object.assign(window, {
+ cancelAddRow,
+ showContractPopup,
+ showPairing,
+ showWallet,
+ showData,
+ addWalletRow,
+ confirmWalletRow,
+ cancelWalletRow,
+ saveDataRowToLocalStorage,
+ saveDeviceName,
+ saveRowToLocalStorage,
+ saveName,
+ updateTableContent,
+ updateDataTableContent,
+ openAvatarPopup,
+ closeAvatarPopup,
+ saveUserInfo,
+ saveAvatarToLocalStorage,
+ loadAvatar,
+ loadUserInfo,
+ updateLocalStorageAfterDelete,
+ disableDeleteButtons,
+ updateBannerContent,
+ generateEmojiList,
+ addressToEmoji,
+ isValidRole,
+ showNotifications,
+ confirmDeleteAccount,
+ deleteAccount,
+ updateNavbarBanner,
+ saveBannerToLocalStorage,
+ loadSavedBanner,
+ closeNotificationPopup,
+ markAsRead,
+ calculateNotifications,
+ exportRecovery,
+ exportUserData,
+ addRow,
+ confirmRow,
+ cancelRow,
+ deleteRow,
+ editDeviceName,
+ showAlert,
+ initializeEventListeners,
+ showProcess,
+ showProcessNotifications,
+ generateRandomNotification,
+ handleLogout,
+ updateActionButtons,
+ resetButtonContainer,
+ finishEditing,
+ handleAvatarUpload,
+ STORAGE_KEYS,
+ ALLOWED_ROLES,
+ mockNotifications,
+ mockProcessRows,
+ mockDataRows,
+ mockContracts,
+ defaultRows,
+ updateNavbarName,
+ updateNavbarLastName,
+});
+
+// Export des nouvelles fonctions
+export {
+ cancelAddRow,
+ showContractPopup,
+ showPairing,
+ showWallet,
+ showData,
+ addWalletRow,
+ confirmWalletRow,
+ cancelWalletRow,
+ saveDataRowToLocalStorage,
+ saveDeviceName,
+ saveRowToLocalStorage,
+ saveName,
+ updateTableContent,
+ updateDataTableContent,
+ openAvatarPopup,
+ closeAvatarPopup,
+ saveUserInfo,
+ saveAvatarToLocalStorage,
+ loadAvatar,
+ loadUserInfo,
+ updateLocalStorageAfterDelete,
+ disableDeleteButtons,
+ updateBannerContent,
+ generateEmojiList,
+ addressToEmoji,
+ isValidRole,
+ showNotifications,
+ confirmDeleteAccount,
+ deleteAccount,
+ updateNavbarBanner,
+ saveBannerToLocalStorage,
+ loadSavedBanner,
+ closeNotificationPopup,
+ markAsRead,
+ calculateNotifications,
+ exportRecovery,
+ exportUserData,
+ addRow,
+ confirmRow,
+ cancelRow,
+ deleteRow,
+ editDeviceName,
+ showAlert,
+ initializeEventListeners,
+ showProcess,
+ showProcessNotifications,
+ generateRandomNotification,
+ handleLogout,
+ updateActionButtons,
+ resetButtonContainer,
+ finishEditing,
+ handleAvatarUpload,
+ STORAGE_KEYS,
+ ALLOWED_ROLES,
+ mockNotifications,
+ mockProcessRows,
+ mockDataRows,
+ mockContracts,
+ defaultRows,
+ updateNavbarName,
+ updateNavbarLastName,
+ type Row,
+ type Contract,
+ type WalletRow,
+ type DataRow,
+ type Notification,
+ type NotificationMessage
+};
+
+
+// Assignation des fonctions à l'objet window
+window.showNotifications = showNotifications;
+window.closeNotificationPopup = closeNotificationPopup;
+window.markAsRead = markAsRead;
+window.exportRecovery = exportRecovery;
+window.confirmDeleteAccount = confirmDeleteAccount;
+window.deleteAccount = deleteAccount;
+window.updateNavbarBanner = updateNavbarBanner;
+window.saveBannerToLocalStorage = saveBannerToLocalStorage;
+window.loadSavedBanner = loadSavedBanner;
+window.initializeEventListeners = initializeEventListeners;
+window.showProcessNotifications = showProcessNotifications;
+window.handleLogout = handleLogout;
+window.initializeEventListeners = initializeEventListeners;
+window.loadSavedBanner = loadSavedBanner;
+window.initAccount = initAccount;
+window.openAvatarPopup = openAvatarPopup;
+
+
+// Fonction d'initialisation pour le router
+export function initAccount(): void {
+ const savedName = localStorage.getItem('userName') || 'Luke';
+ const savedLastName = localStorage.getItem('userLastName') || 'Doe';
+
+ const userNameElement = document.querySelector('.user-name');
+ const userLastNameElement = document.querySelector('.user-lastname');
+
+ if (userNameElement) {
+ userNameElement.textContent = savedName;
+ }
+ if (userLastNameElement) {
+ userLastNameElement.textContent = savedLastName;
+ }
+
+ initializeEventListeners();
+ loadSavedBanner();
+ if (!localStorage.getItem('rows')) {
+ localStorage.setItem('rows', JSON.stringify(defaultRows));
+ }
+}
+
+// Expose all necessary functions to window
+window.showPairing = showPairing;
+window.showWallet = showWallet;
+window.showProcess = showProcess;
+window.showData = showData;
+
+// Initialize when the document is loaded
+document.addEventListener('DOMContentLoaded', () => {
+ initAccount();
+ showPairing();
+});
+
diff --git a/src/pages/chat/chat.html b/src/pages/chat/chat.html
new file mode 100644
index 0000000..9d524e0
--- /dev/null
+++ b/src/pages/chat/chat.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ Messagerie
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/chat/chat.ts b/src/pages/chat/chat.ts
new file mode 100644
index 0000000..55910be
--- /dev/null
+++ b/src/pages/chat/chat.ts
@@ -0,0 +1,373 @@
+import { groupsMock } from '../../mocks/mock-signature/groupsMock.js';
+import { messageStore } from '../../utils/messageMock.js';
+
+let messagesMock = messageStore.getMessages();
+
+let selectedMemberId: number | null = null;
+
+// Load the list of groups
+function loadGroupList() {
+ const groupList = document.getElementById('group-list');
+ if (!groupList) return;
+
+ groupsMock.forEach(group => {
+ const li = document.createElement('li');
+ li.textContent = group.name;
+ li.onclick = (event) => {
+ event.stopPropagation();
+ toggleRoles(group, li);
+ };
+ groupList.appendChild(li);
+ });
+}
+
+// Toggle the list of Roles
+function toggleRoles(group: { roles: { name: string; members: any[] }[] }, groupElement: HTMLElement) {
+ let roleList = groupElement.querySelector('.role-list') as HTMLElement;
+ if (roleList) {
+ roleList.style.display = roleList.style.display === 'none' ? 'block' : 'none';
+ return;
+ }
+
+ roleList = document.createElement('ul');
+ roleList.className = 'role-list';
+
+ group.roles.forEach(role => {
+ const roleItem = document.createElement('li');
+ roleItem.textContent = role.name;
+
+ roleItem.onclick = (event) => {
+ event.stopPropagation();
+ toggleMembers(role, roleItem);
+ };
+
+ roleList.appendChild(roleItem);
+ });
+
+ groupElement.appendChild(roleList);
+}
+
+// Toggle the list of membres
+function toggleMembers(role: { members: { id: number; name: string; }[] }, roleElement: HTMLElement) {
+ let memberList = roleElement.querySelector('.member-list') as HTMLElement;
+ 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();
+ loadMemberChat(member.id);
+ };
+
+ memberList.appendChild(memberItem);
+ });
+
+ roleElement.appendChild(memberList);
+}
+
+// Load the list of members
+function loadMemberChat(memberId: string | number) {
+ selectedMemberId = Number(memberId);
+ const memberMessages = messagesMock.find(m => String(m.memberId) === String(memberId));
+
+ // Trouver le processus et le rôle du membre
+ let memberInfo = { processName: '', roleName: '', memberName: '' };
+ groupsMock.forEach(process => {
+ process.roles.forEach(role => {
+ const member = role.members.find(m => String(m.id) === String(memberId));
+ if (member) {
+ memberInfo = {
+ processName: process.name,
+ roleName: role.name,
+ memberName: member.name
+ };
+ }
+ });
+ });
+
+ const chatHeader = document.getElementById('chat-header');
+ const messagesContainer = document.getElementById('messages');
+
+ if (!chatHeader || !messagesContainer) return;
+
+ chatHeader.textContent = `Chat with ${memberInfo.roleName} ${memberInfo.memberName} from ${memberInfo.processName}`;
+ messagesContainer.innerHTML = '';
+
+ if (memberMessages) {
+ memberMessages.messages.forEach((message: {
+ type: string;
+ fileData?: string;
+ fileName?: string;
+ sender: string;
+ text?: string;
+ time: string;
+ }) => {
+ const messageElement = document.createElement('div');
+ messageElement.className = 'message-container';
+
+ const messageContent = document.createElement('div');
+ messageContent.className = 'message';
+ if (message.type === 'file') {
+ messageContent.innerHTML = `${message.fileName} `;
+ messageContent.classList.add('user');
+ } else {
+ messageContent.innerHTML = `${message.sender} : ${message.text} ${message.time} `;
+ if (message.sender === "4NK") {
+ messageContent.classList.add('user');
+ }
+ }
+
+ messageElement.appendChild(messageContent);
+ messagesContainer.appendChild(messageElement);
+ });
+ }
+
+
+ scrollToBottom(messagesContainer);
+}
+
+// Scroll down the conversation after loading messages
+function scrollToBottom(container: HTMLElement) {
+ container.scrollTop = container.scrollHeight;
+}
+
+// Generate an automatic response
+function generateAutoReply(senderName: string) {
+ return {
+ id: Date.now(),
+ sender: senderName,
+ text: "OK...",
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
+ type: 'text'
+ };
+}
+
+// Send a messsage
+function sendMessage() {
+ const messageInput = document.getElementById('message-input') as HTMLInputElement;
+ if (!messageInput) return;
+ const messageText = messageInput.value.trim();
+
+ if (messageText === '' || selectedMemberId === null) {
+ return;
+ }
+
+ const newMessage = {
+ id: Date.now(),
+ sender: "4NK",
+ text: messageText,
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
+ type: 'text'
+ };
+
+ // Ajouter et afficher le message immédiatement
+ messageStore.addMessage(selectedMemberId, newMessage);
+ messagesMock = messageStore.getMessages();
+ loadMemberChat(selectedMemberId);
+
+ // Réinitialiser l'input
+ messageInput.value = '';
+
+ // Réponse automatique après 2 secondes
+ setTimeout(() => {
+ if (selectedMemberId === null) return;
+
+ const autoReply = generateAutoReply(`Member ${selectedMemberId}`);
+ messageStore.addMessage(selectedMemberId, autoReply);
+ messagesMock = messageStore.getMessages();
+ loadMemberChat(selectedMemberId);
+ addNotification(selectedMemberId, autoReply);
+ }, 2000);
+}
+
+// Add an event for the submit button
+const sendBtn = document.getElementById('send-button');
+const messageInput = document.getElementById('message-input');
+
+if (sendBtn) sendBtn.onclick = sendMessage;
+if (messageInput) {
+ messageInput.addEventListener('keydown', function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ sendMessage();
+ }
+ });
+}
+
+// Send a file
+function sendFile(file: File) {
+ const reader = new FileReader();
+ reader.onloadend = function () {
+ const fileData = reader.result;
+ const fileName = file.name;
+
+ const newFileMessage = {
+ id: Date.now(),
+ sender: "4NK",
+ fileName: fileName,
+ fileData: fileData,
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
+ type: 'file'
+ };
+
+ if (selectedMemberId === null) return;
+ messageStore.addMessage(selectedMemberId, newFileMessage);
+
+ messagesMock = messageStore.getMessages();
+
+ if (selectedMemberId !== null) {
+ loadMemberChat(selectedMemberId);
+ }
+ };
+
+ reader.readAsDataURL(file);
+}
+
+// Managing the sent file
+document.getElementById('file-input')?.addEventListener('change', function (event: Event) {
+ const file = (event.target as HTMLInputElement).files?.[0];
+ if (file) {
+ sendFile(file);
+ }
+});
+
+
+///////////////////// Notification module /////////////////////
+const notificationBadge = document.querySelector('.notification-badge') as HTMLElement;
+const notificationBoard = document.getElementById('notification-board');
+const notificationBell = document.getElementById('notification-bell');
+
+interface Notification {
+ memberId: number;
+ text: string;
+ time: string;
+}
+
+let notifications: Notification[] = [];
+let unreadCount = 0;
+// Update notification badge
+function updateNotificationBadge() {
+ if (!notificationBadge) return;
+ const count = notifications.length;
+ notificationBadge.textContent = count > 99 ? '+99' : count.toString();
+ notificationBadge.style.display = count > 0 ? 'block' : 'none';
+}
+
+// Add notification
+function addNotification(memberId: number, message: { text: string; time: string }) {
+ // Creating a new notification
+ const notification = {
+ memberId,
+ text: `New message from Member ${memberId}: ${message.text}`,
+ time: message.time
+ };
+
+ // Added notification to list and interface
+ notifications.push(notification);
+ renderNotifications();
+ updateNotificationBadge();
+}
+
+// Show notifications
+function renderNotifications() {
+ if (!notificationBoard) return;
+
+ // Reset the interface
+ notificationBoard.innerHTML = '';
+
+ // Displays "No notifications available" if there are no notifications
+ if (notifications.length === 0) {
+ notificationBoard.innerHTML = 'No notifications available
';
+ return;
+ }
+
+ // Add each notification to the list
+ notifications.forEach((notif, index) => {
+ const notifElement = document.createElement('div');
+ notifElement.className = 'notification-item';
+ notifElement.textContent = `${notif.text} at ${notif.time}`;
+ notifElement.onclick = () => {
+ loadMemberChat(notif.memberId);
+ removeNotification(index);
+ };
+ notificationBoard.appendChild(notifElement);
+ });
+}
+
+// Delete a notification
+function removeNotification(index: number) {
+ notifications.splice(index, 1);
+ renderNotifications();
+ updateNotificationBadge();
+}
+
+// Adds an event for deploying the notification list
+if (notificationBell && notificationBoard) {
+ notificationBell.onclick = () => {
+ notificationBoard.style.display = notificationBoard.style.display === 'block' ? 'none' : 'block';
+ };
+}
+
+// Close the notification board when clicking outside of it
+document.addEventListener('click', (event) => {
+ if (notificationBoard && notificationBell &&
+ notificationBoard.style.display === 'block' &&
+ !notificationBoard.contains(event.target as Node) &&
+ !notificationBell.contains(event.target as Node)) {
+ notificationBoard.style.display = 'none';
+ }
+});
+
+// Loads group list at startup
+//loadGroupList();
+
+export function initChat(): void {
+ loadGroupList();
+
+ // Re-initialize event listeners
+ const sendBtn = document.getElementById('send-button');
+ const messageInput = document.getElementById('message-input');
+ const fileInput = document.getElementById('file-input');
+
+ if (sendBtn) sendBtn.onclick = sendMessage;
+ if (messageInput) {
+ messageInput.addEventListener('keydown', function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ sendMessage();
+ }
+ });
+ }
+
+ if (fileInput) {
+ fileInput.addEventListener('change', function (event: Event) {
+ const file = (event.target as HTMLInputElement).files?.[0];
+ if (file) {
+ sendFile(file);
+ }
+ });
+ }
+
+ // Initialize notification listeners
+ const notificationBell = document.getElementById('notification-bell');
+ const notificationBoard = document.getElementById('notification-board');
+
+ if (notificationBell && notificationBoard) {
+ notificationBell.onclick = () => {
+ notificationBoard.style.display = notificationBoard.style.display === 'block' ? 'none' : 'block';
+ };
+ }
+}
+
+function saveMessagesToLocalStorage(messages: any[]) {
+ messageStore.setMessages(messages);
+ messagesMock = messageStore.getMessages();
+}
diff --git a/src/pages/signature/signature.html b/src/pages/signature/signature.html
new file mode 100644
index 0000000..4b1f64b
--- /dev/null
+++ b/src/pages/signature/signature.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ Signatures
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/signature/signature.ts b/src/pages/signature/signature.ts
new file mode 100644
index 0000000..95feee4
--- /dev/null
+++ b/src/pages/signature/signature.ts
@@ -0,0 +1,1209 @@
+declare global {
+ interface Window {
+ toggleUserList: typeof toggleUserList;
+ switchUser: typeof switchUser;
+ closeProcessDetails: typeof closeProcessDetails;
+ loadMemberChat: typeof loadMemberChat;
+ closeRoleDocuments: typeof closeRoleDocuments;
+ newRequest: typeof newRequest;
+ submitRequest: typeof submitRequest;
+ closeNewRequest: typeof closeNewRequest;
+ removeMember: typeof removeMember;
+ closeModal: typeof closeModal;
+ submitDocumentRequest: typeof submitDocumentRequest;
+ submitNewDocument: typeof submitNewDocument;
+ }
+}
+
+import { groupsMock } from '../../mocks/mock-signature/groupsMock';
+import { messagesMock as initialMessagesMock } from '../../mocks/mock-signature/messagesMock';
+import { membersMock } from '../../mocks/mock-signature/membersMocks';
+import {
+ Group,
+ Message,
+ MemberMessages,
+ DocumentSignature,
+ RequestParams,
+ Notification as ImportedNotification
+} from '../../models/signature.models';
+import { messageStore } from '../../utils/messageMock';
+import { showAlert } from '../account/account';
+let currentUser = membersMock[0];
+
+// Fonction pour gérer la liste des utilisateurs
+function toggleUserList() {
+ const userList = document.getElementById('userList');
+ if (!userList) return;
+
+ if (!userList.classList.contains('show')) {
+ // Remplir la liste des utilisateurs
+ userList.innerHTML = membersMock.map(member => `
+
+
${member.avatar}
+
+ ${member.name}
+ ${member.email}
+
+
+ `).join('');
+ }
+ userList?.classList.toggle('show');
+}
+
+// Fonction pour changer d'utilisateur
+function switchUser(userId: string) {
+ const user = membersMock.find(member => member.id === userId);
+ if (!user) return;
+ currentUser = user;
+ updateCurrentUserDisplay();
+ // Recharger la vue si nécessaire
+ const userList = document.getElementById('userList');
+ userList?.classList.remove('show');
+}
+
+// Fonction pour mettre à jour l'affichage de l'utilisateur
+function updateCurrentUserDisplay() {
+ const userDisplay = document.getElementById('current-user');
+ if (userDisplay) {
+ userDisplay.innerHTML = `
+
+ ${currentUser.avatar}
+ ${currentUser.name}
+
+ `;
+ }
+}
+
+// Ajouter les fonctions au scope global
+window.toggleUserList = toggleUserList;
+window.switchUser = switchUser;
+
+// Initialiser l'affichage de l'utilisateur courant au chargement
+document.addEventListener('DOMContentLoaded', () => {
+ updateCurrentUserDisplay();
+
+ // Fermer la liste si on clique ailleurs
+ document.addEventListener('click', (event) => {
+ const userList = document.getElementById('userList');
+ const userSwitchBtn = document.getElementById('userSwitchBtn');
+ if (userSwitchBtn && userList && !userSwitchBtn.contains(event.target as Node) && !userList.contains(event.target as Node)) {
+ userList.classList.remove('show');
+ }
+ });
+
+ // Initialiser les groupes en localStorage s'ils n'existent pas
+ if (!localStorage.getItem('groups')) {
+ localStorage.setItem('groups', JSON.stringify(groupsMock));
+ }
+});
+
+let messagesMock = messageStore.getMessages();
+if (messagesMock.length === 0) {
+ messageStore.setMessages(initialMessagesMock);
+ messagesMock = messageStore.getMessages();
+}
+
+
+let selectedMemberId: string | null = null;
+
+// Load the list of groups
+function loadGroupList() {
+ const groupList = document.getElementById('group-list');
+ if (!groupList) return;
+
+ groupsMock.forEach(group => {
+ const li = document.createElement('li');
+ li.className = 'group-list-item';
+
+ // Créer un conteneur flex pour le nom et l'icône
+ const container = document.createElement('div');
+ container.className = 'group-item-container';
+
+ // Span pour le nom du processus
+ const nameSpan = document.createElement('span');
+ nameSpan.textContent = group.name;
+ nameSpan.className = 'process-name';
+ nameSpan.onclick = (event) => {
+ event.stopPropagation();
+ toggleRoles(group, li);
+ };
+
+ // Ajouter l'icône ⚙️ avec un ID unique
+ const settingsIcon = document.createElement('span');
+ settingsIcon.textContent = '⚙️';
+ settingsIcon.className = 'settings-icon';
+ settingsIcon.id = `settings-${group.id}`; // ID unique basé sur l'ID du groupe
+
+ // Créer une div pour la vue détaillée avec un ID unique correspondant
+ const detailsArea = document.createElement('div');
+ detailsArea.id = `process-details-${group.id}`;
+ detailsArea.className = 'process-details';
+ detailsArea.style.display = 'none';
+
+ settingsIcon.onclick = (event) => {
+ event.stopPropagation();
+ showProcessDetails(group, group.id);
+ };
+
+ // Assembler les éléments
+ container.appendChild(nameSpan);
+ container.appendChild(settingsIcon);
+ li.appendChild(container);
+ groupList.appendChild(li);
+ });
+}
+
+// Toggle the list of Roles
+function toggleRoles(group: Group, groupElement: HTMLElement) {
+ let roleList = groupElement.querySelector('.role-list');
+ if (roleList) {
+ (roleList as HTMLElement).style.display = (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none';
+ return;
+ }
+
+ roleList = document.createElement('ul');
+ roleList.className = 'role-list';
+
+ group.roles.forEach(role => {
+ const roleItem = document.createElement('li');
+
+ // Créer un conteneur flex pour le nom et l'icône
+ const container = document.createElement('div');
+ container.className = 'role-item-container';
+ container.style.display = 'flex';
+ container.style.justifyContent = 'space-between';
+ container.style.alignItems = 'center';
+
+ // Span pour le nom du rôle
+ const nameSpan = document.createElement('span');
+ nameSpan.textContent = role.name;
+
+ // Bouton dossier
+ const folderButton = document.createElement('span');
+ folderButton.textContent = '📁';
+ folderButton.className = 'folder-icon';
+ folderButton.onclick = (event) => {
+ event.stopPropagation();
+ showRoleDocuments(role, group);
+ };
+
+ // Assembler les éléments
+ container.appendChild(nameSpan);
+ container.appendChild(folderButton);
+ roleItem.appendChild(container);
+
+ roleItem.onclick = (event) => {
+ event.stopPropagation();
+ toggleMembers(role, roleItem);
+ };
+
+ roleList.appendChild(roleItem);
+ });
+
+ groupElement.appendChild(roleList);
+}
+
+// Toggle the list of membres
+function toggleMembers(role: { members: { id: string | number; name: string; }[] }, roleElement: HTMLElement) {
+ let memberList = roleElement.querySelector('.member-list');
+ if (memberList) {
+ (memberList as HTMLElement).style.display = (memberList as HTMLElement).style.display === 'none' ? 'block' : 'none';
+ return;
+ }
+
+ memberList = document.createElement('ul');
+ memberList.className = 'member-list';
+
+ role.members.forEach(member => {
+ const memberItem = document.createElement('li');
+ memberItem.textContent = member.name;
+
+ memberItem.onclick = (event) => {
+ event.stopPropagation();
+ loadMemberChat(member.id.toString());
+ };
+
+ memberList.appendChild(memberItem);
+ });
+
+ roleElement.appendChild(memberList);
+}
+
+// Load the list of members
+function loadMemberChat(memberId: string | number) {
+ selectedMemberId = String(memberId);
+ const memberMessages = messagesMock.find(m => String(m.memberId) === String(memberId));
+
+ // Trouver le processus et le rôle du membre
+ let memberInfo = { processName: '', roleName: '', memberName: '' };
+ groupsMock.forEach(process => {
+ process.roles.forEach(role => {
+ const member = role.members.find(m => String(m.id) === String(memberId));
+ if (member) {
+ memberInfo = {
+ processName: process.name,
+ roleName: role.name,
+ memberName: member.name
+ };
+ }
+ });
+ });
+
+ const chatHeader = document.getElementById('chat-header');
+ const messagesContainer = document.getElementById('messages');
+
+ if (!chatHeader || !messagesContainer) return;
+
+ chatHeader.textContent = `Chat with ${memberInfo.roleName} ${memberInfo.memberName} from ${memberInfo.processName}`;
+ messagesContainer.innerHTML = '';
+
+ if (memberMessages) {
+ memberMessages.messages.forEach((message: Message) => {
+ const messageElement = document.createElement('div');
+ messageElement.className = 'message-container';
+
+ const messageContent = document.createElement('div');
+ messageContent.className = 'message';
+ if (message.type === 'file') {
+ messageContent.innerHTML = `${message.fileName} `;
+ messageContent.classList.add('user');
+ } else {
+ messageContent.innerHTML = `${message.sender} : ${message.text} ${message.time} `;
+ if (message.sender === "4NK") {
+ messageContent.classList.add('user');
+ }
+ }
+
+ messageElement.appendChild(messageContent);
+ messagesContainer.appendChild(messageElement);
+ });
+ }
+
+
+ scrollToBottom(messagesContainer);
+}
+
+// Scroll down the conversation after loading messages
+function scrollToBottom(container: HTMLElement) {
+ container.scrollTop = container.scrollHeight;
+}
+
+// Generate an automatic response
+function generateAutoReply(senderName: string): Message {
+ return {
+ id: Date.now(),
+ sender: senderName,
+ text: "OK...",
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
+ type: 'text' as const
+ };
+}
+
+// Send a messsage
+function sendMessage() {
+ const messageInput = document.getElementById('message-input') as HTMLInputElement;
+ if (!messageInput) return;
+ const messageText = messageInput.value.trim();
+
+ if (messageText === '' || selectedMemberId === null) {
+ return;
+ }
+
+ const newMessage: Message = {
+ id: Date.now(),
+ sender: "4NK",
+ text: messageText,
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
+ type: 'text' as const
+ };
+
+ // Ajouter et afficher le message immédiatement
+ messageStore.addMessage(selectedMemberId, newMessage);
+ messagesMock = messageStore.getMessages();
+ loadMemberChat(selectedMemberId);
+
+ // Réinitialiser l'input
+ messageInput.value = '';
+
+ // Réponse automatique après 2 secondes
+ setTimeout(() => {
+ if (selectedMemberId) {
+ const autoReply = generateAutoReply(`Member ${selectedMemberId}`);
+ messageStore.addMessage(selectedMemberId, autoReply);
+ messagesMock = messageStore.getMessages();
+ loadMemberChat(selectedMemberId);
+ addNotification(selectedMemberId, autoReply);
+ }
+ }, 2000);
+}
+
+// Add an event for the submit button
+const sendBtn = document.getElementById('send-button');
+if (sendBtn) sendBtn.onclick = sendMessage;
+
+const messageInput = document.getElementById('message-input');
+if (messageInput) {
+ messageInput.addEventListener('keydown', function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ sendMessage();
+ }
+ });
+}
+
+// Send a file
+function sendFile(file: File) {
+ const reader = new FileReader();
+ reader.onloadend = function () {
+ const fileData = reader.result;
+ const fileName = file.name;
+
+ const newFileMessage = {
+ id: Date.now(),
+ sender: "4NK",
+ fileName: fileName,
+ fileData: fileData,
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
+ type: 'file'
+ };
+
+ if (selectedMemberId) {
+ messageStore.addMessage(selectedMemberId, newFileMessage);
+ }
+
+ messagesMock = messageStore.getMessages();
+
+ if (selectedMemberId) {
+ loadMemberChat(selectedMemberId);
+ }
+ };
+
+ reader.readAsDataURL(file);
+}
+
+// Managing the sent file
+document.getElementById('file-input')?.addEventListener('change', function (event) {
+ const file = (event.target as HTMLInputElement).files?.[0];
+ if (file) {
+ sendFile(file);
+ }
+});
+
+
+///////////////////// Notification module /////////////////////
+const notificationBadge = document.querySelector('.notification-badge');
+const notificationBoard = document.getElementById('notification-board');
+const notificationBell = document.getElementById('notification-bell');
+
+
+interface LocalNotification {
+ memberId: string;
+ text: string;
+ time: string;
+}
+
+let notifications: LocalNotification[] = [];
+let unreadCount = 0;
+// Update notification badge
+function updateNotificationBadge() {
+ if (!notificationBadge) return;
+ const count = notifications.length;
+ notificationBadge.textContent = count > 99 ? '+99' : count.toString();
+ (notificationBadge as HTMLElement).style.display = count > 0 ? 'block' : 'none';
+}
+
+// Add notification
+function addNotification(memberId: string, message: Message) {
+ // Creating a new notification
+ const notification = {
+ memberId,
+ text: `New message from Member ${memberId}: ${message.text}`,
+ time: message.time
+ };
+
+ // Added notification to list and interface
+ notifications.push(notification);
+ renderNotifications();
+ updateNotificationBadge();
+}
+
+// Show notifications
+function renderNotifications() {
+ if (!notificationBoard) return;
+
+ // Reset the interface
+ notificationBoard.innerHTML = '';
+
+ // Displays "No notifications available" if there are no notifications
+ if (notifications.length === 0) {
+ notificationBoard.innerHTML = 'No notifications available
';
+ return;
+ }
+
+ // Add each notification to the list
+ notifications.forEach((notif, index) => {
+ const notifElement = document.createElement('div');
+ notifElement.className = 'notification-item';
+ notifElement.textContent = `${notif.text} at ${notif.time}`;
+ notifElement.onclick = () => {
+ loadMemberChat(notif.memberId);
+ removeNotification(index);
+ };
+ notificationBoard.appendChild(notifElement);
+ });
+}
+
+// Delete a notification
+function removeNotification(index: number) {
+ notifications.splice(index, 1);
+ renderNotifications();
+ updateNotificationBadge();
+}
+
+// Adds an event for deploying the notification list
+if (notificationBell && notificationBoard) {
+ notificationBell.onclick = () => {
+ notificationBoard.style.display = notificationBoard.style.display === 'block' ? 'none' : 'block';
+ };
+}
+
+// Close the notification board when clicking outside of it
+document.addEventListener('click', (event) => {
+ if (notificationBoard && notificationBoard.style.display === 'block' &&
+ !notificationBoard.contains(event.target as Node) &&
+ notificationBell && !notificationBell.contains(event.target as Node)) {
+ notificationBoard.style.display = 'none';
+ }
+});
+
+// ------------------ PROCESS DETAILS ------------------
+// Fonction pour afficher les détails du processus
+function showProcessDetails(group: Group, groupId: number) {
+ console.log('Showing details for group:', groupId);
+
+ // Fermer toutes les vues de processus existantes
+ const allDetailsAreas = document.querySelectorAll('.process-details');
+ allDetailsAreas.forEach(area => {
+ (area as HTMLElement).style.display = 'none';
+ });
+
+ const container = document.querySelector('.container');
+ if (!container) {
+ console.error('Container not found');
+ return;
+ }
+
+ let detailsArea = document.getElementById(`process-details-${groupId}`);
+ if (!detailsArea) {
+ detailsArea = document.createElement('div');
+ detailsArea.id = `process-details-${groupId}`;
+ detailsArea.className = 'process-details';
+ container.appendChild(detailsArea);
+ }
+
+ if (detailsArea) {
+ detailsArea.style.display = 'block';
+ detailsArea.innerHTML = `
+
+
+
+
Description
+
${group.description || 'No description available'}
+
+
+
Roles and Documents
+ ${group.roles.map(role => `
+
+
${role.name}
+
+ ${(role.documents || []).length > 0 ?
+ (role.documents || []).map(document => {
+ const totalSignatures = document.signatures.length;
+ const signedCount = document.signatures.filter((sig: DocumentSignature) => sig.signed).length;
+ const percentage = totalSignatures > 0 ? (signedCount / totalSignatures) * 100 : 0;
+
+ // Détection d'un document vierge
+ const isVierge = !document.createdAt ||
+ !document.deadline ||
+ document.signatures.length === 0;
+
+ return `
+
+
+
+ ${!isVierge ? `
+
Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}
+
Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}
+
Duration: ${calculateDuration(document.createdAt || '', document.deadline || '')} days
+ ` : '
Document vierge - En attente de création
'}
+
+ ${!isVierge ? `
+
+
Signatures:
+
+ ${document.signatures.map((sig: DocumentSignature) => `
+
+ ${sig.member.name}
+
+ ${sig.signed ?
+ `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'Unknown date'}` :
+ ' Pending'}
+
+
+ `).join('')}
+
+
+
${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)
+
+ ` : ''}
+
+ `;
+ }).join('')
+ : '
No documents available for this role
'}
+
+
+ `).join('')}
+
+
+
Members by Role
+
+ ${group.roles.map(role => `
+
+
${role.name}
+
+ ${role.members.map(member => `
+ ${member.name}
+ `).join('')}
+
+
+ `).join('')}
+
+
+
+ `;
+
+ /**const newRequestBtn = document.createElement('button');
+ newRequestBtn.className = 'new-request-btn';
+ newRequestBtn.textContent = 'New request';
+ newRequestBtn.onclick = () => newRequest(group);**/
+
+ const newCloseProcessButton = document.createElement('button');
+ newCloseProcessButton.className = 'close-btn';
+ newCloseProcessButton.textContent = 'x';
+ newCloseProcessButton.addEventListener('click', () => closeProcessDetails(groupId));
+
+ const headerButtons = detailsArea.querySelector('.header-buttons');
+ if (headerButtons) {
+ /**headerButtons.appendChild(newRequestBtn);**/
+ headerButtons.appendChild(newCloseProcessButton);
+ }
+ }
+}
+
+// Fonction pour fermer les détails et revenir au chat
+function closeProcessDetails(groupId: number) {
+ const detailsArea = document.getElementById(`process-details-${groupId}`);
+ const chatArea = document.getElementById('chat-area');
+
+ if (detailsArea) {
+ detailsArea.style.display = 'none';
+ }
+
+ if (chatArea) {
+ chatArea.style.display = 'block';
+ }
+}
+
+window.closeProcessDetails = closeProcessDetails;
+window.loadMemberChat = loadMemberChat;
+
+// Nouvelle fonction pour afficher les documents d'un rôle
+function showRoleDocuments(role: {
+ name: string;
+ documents?: Array<{
+ name: string;
+ visibility: string;
+ createdAt: string | null | undefined;
+ deadline: string | null | undefined;
+ signatures: DocumentSignature[];
+ id: number;
+ description?: string;
+ status?: string;
+ }>;
+ id?: number;
+}, group: Group) {
+ console.log('Showing documents for role:', role.name, 'in group:', group.name);
+ // Fermer d'abord toutes les vues de documents existantes
+ const allDetailsAreas = document.querySelectorAll('.process-details');
+ allDetailsAreas.forEach(area => {
+ area.remove();
+ });
+
+ const container = document.querySelector('.container');
+ if (!container) {
+ console.error('Container not found');
+ return;
+ }
+
+ // Créer une nouvelle zone de détails
+ const detailsArea = document.createElement('div');
+ detailsArea.id = `role-documents-${role.name}`;
+ detailsArea.className = 'process-details';
+
+ detailsArea.innerHTML = `
+
+
+
+
Documents
+
+ ${(role.documents || []).length > 0 ?
+ (role.documents || []).map(document => {
+ const totalSignatures = document.signatures.length;
+ const signedCount = document.signatures.filter((sig: DocumentSignature) => sig.signed).length;
+ const percentage = totalSignatures > 0 ? (signedCount / totalSignatures) * 100 : 0;
+ const isVierge = !document.createdAt ||
+ !document.deadline ||
+ document.signatures.length === 0;
+
+ return `
+
+
+
+ ${!isVierge ? `
+
Created on: ${document.createdAt ? new Date(document.createdAt).toLocaleDateString() : 'N/A'}
+
Deadline: ${document.deadline ? new Date(document.deadline).toLocaleDateString() : 'N/A'}
+
Duration: ${calculateDuration(document.createdAt, document.deadline)} days
+
+
Signatures:
+
+ ${document.signatures.map((sig: DocumentSignature) => `
+
+ ${sig.member.name}
+
+ ${sig.signed ?
+ `✓ Signed on ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'Unknown date'}` :
+ '⌛ Pending'}
+
+
+ `).join('')}
+
+
+
${signedCount} out of ${totalSignatures} signed (${percentage.toFixed(0)}%)
+
+ ` : `
+
Document vierge - En attente de création
+
New request
+ `}
+
+
+ `;
+ }).join('')
+ : '
No documents available for this role
'
+ }
+
+
+
+ `;
+
+ container.appendChild(detailsArea);
+}
+
+// Fonction pour fermer la vue des documents d'un rôle
+function closeRoleDocuments(roleName: string) {
+ const detailsArea = document.getElementById(`role-documents-${roleName}`);
+ if (detailsArea) {
+
+ detailsArea.remove();
+ }
+}
+
+window.closeRoleDocuments = closeRoleDocuments;
+
+
+window.switchUser = switchUser;
+
+// Fonction pour calculer la durée entre deux dates
+function calculateDuration(startDate: string | null | undefined, endDate: string | null | undefined): number {
+ const start = new Date(startDate || '');
+ const end = new Date(endDate || '');
+ const duration = end.getTime() - start.getTime();
+ return Math.floor(duration / (1000 * 60 * 60 * 24));
+}
+
+
+window.newRequest = newRequest;
+window.submitRequest = submitRequest;
+window.closeNewRequest = closeNewRequest;
+
+// Définir la fonction removeMember pour supprimer un membre par son ID
+function removeMember(memberId: string | number) {
+ const memberElement = document.querySelector(`.selected-member[data-member-id="${memberId}"]`);
+ if (memberElement) {
+ memberElement.remove();
+ }
+}
+
+window.removeMember = removeMember;
+
+
+// Fonction pour gérer la nouvelle demande
+function newRequest(params: RequestParams) {
+ const modal = document.createElement('div');
+ modal.className = 'modal-overlay';
+
+ // Modifier cette partie pour chercher par name au lieu de id
+ const process = groupsMock.find(g => g.id === params.processId);
+ const role = process?.roles.find(r => r.name === params.roleName);
+
+ // Récupérer les membres du rôle
+ const roleMembers = role?.members ? role.members.map(member => member.id) : [];
+
+ modal.innerHTML = `
+
+ `;
+
+ document.body.appendChild(modal);
+
+ const dropZone = modal.querySelector('#dropZone') as HTMLDivElement;
+ const fileInput = modal.querySelector('#fileInput') as HTMLInputElement;
+ const fileList = modal.querySelector('#fileList') as HTMLDivElement;
+
+ // Rendre la zone cliquable
+ dropZone.addEventListener('click', () => {
+ fileInput.click();
+ });
+
+ // Gérer la sélection de fichiers
+ fileInput.addEventListener('change', (e: Event) => {
+ const target = e.target as HTMLInputElement;
+ if (target.files && target.files.length > 0) {
+ handleFiles(target.files);
+ }
+ });
+
+ // Gérer le drag & drop
+ dropZone.addEventListener('dragover', (e: DragEvent) => {
+ e.preventDefault();
+ dropZone.classList.add('dragover');
+ });
+
+ dropZone.addEventListener('dragleave', () => {
+ dropZone.classList.remove('dragover');
+ });
+
+ dropZone.addEventListener('drop', (e: DragEvent) => {
+ e.preventDefault();
+ dropZone.classList.remove('dragover');
+ if (e.dataTransfer?.files) {
+ handleFiles(e.dataTransfer.files);
+ }
+ });
+
+ function handleFiles(files: FileList) {
+ Array.from(files).forEach(file => {
+ // Vérifier si le fichier n'est pas déjà dans la liste
+ const existingFiles = fileList.querySelectorAll('.file-name');
+ const isDuplicate = Array.from(existingFiles).some(
+ existingFile => existingFile.textContent === file.name
+ );
+
+ if (!isDuplicate) {
+ const fileItem = document.createElement('div');
+ fileItem.className = 'file-item';
+ fileItem.innerHTML = `
+
+ ${file.name}
+ (${(file.size / 1024).toFixed(1)} KB)
+
+ ×
+ `;
+
+ // Ajouter l'événement de suppression
+ const removeBtn = fileItem.querySelector('.remove-file');
+ if (removeBtn) {
+ removeBtn.addEventListener('click', () => fileItem.remove());
+ }
+
+ fileList.appendChild(fileItem);
+ }
+ });
+ }
+}
+
+function closeModal(button: HTMLElement) {
+ const modalOverlay = button.closest('.modal-overlay');
+ if (modalOverlay) {
+ modalOverlay.remove();
+ }
+}
+
+window.closeModal = closeModal;
+
+let selectedSignatories: DocumentSignature[] = [];
+
+function submitNewDocument(event: Event) {
+ event.preventDefault();
+
+ const form = document.getElementById('newDocumentForm') as HTMLFormElement;
+ if (!form) {
+ showAlert('Form not found');
+ return;
+ }
+
+ // Get form values
+ const processId = Number((form.querySelector('#processId') as HTMLInputElement)?.value);
+ const documentId = Number((form.querySelector('#documentId') as HTMLInputElement)?.value);
+ const documentName = (form.querySelector('#documentName') as HTMLInputElement)?.value?.trim();
+ const description = (form.querySelector('#description') as HTMLTextAreaElement)?.value?.trim();
+ const deadline = (form.querySelector('#deadline') as HTMLInputElement)?.value;
+ const visibility = (form.querySelector('#visibility') as HTMLSelectElement)?.value;
+
+ // Validation
+ if (!documentName || !description || !deadline) {
+ showAlert('Please fill in all required fields');
+ return;
+ }
+
+ // Mettre à jour à la fois le mock local et le localStorage
+ let groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock));
+ const group = groups.find((g: Group) => g.id === processId);
+
+ if (!group) {
+ showAlert('Process not found');
+ return;
+ }
+
+ // Trouver le rôle qui contient le document
+ const role = group.roles.find((r: any) => {
+ return r.documents?.some((d: any) => d.id === documentId);
+ });
+
+ if (!role) {
+ showAlert('Role not found');
+ return;
+ }
+
+ const documentIndex = role.documents.findIndex((d: any) => d.id === documentId);
+
+ const updatedDocument = {
+ id: documentId,
+ name: documentName,
+ description: description,
+ createdAt: new Date().toISOString(),
+ deadline: deadline,
+ visibility: visibility,
+ status: "pending",
+ signatures: role.members.map((member: { id: string | number; name: string }) => ({
+ member: member,
+ signed: false,
+ signedAt: null
+ }))
+ };
+
+ // Mettre à jour le document dans le rôle
+ if (documentIndex !== -1) {
+ role.documents[documentIndex] = updatedDocument;
+ }
+
+ // Mettre à jour le localStorage et le mock global
+ localStorage.setItem('groups', JSON.stringify(groups));
+ Object.assign(groupsMock, groups);
+
+ // Fermer le modal
+ if (event.target instanceof HTMLElement) {
+ closeModal(event.target);
+ }
+
+ // Recharger la vue des documents
+ showRoleDocuments(role, group);
+
+ showAlert('Document updated successfully!');
+}
+
+window.submitNewDocument = submitNewDocument;
+
+
+function submitRequest() {
+
+ showAlert("Request submitted!");
+}
+
+function closeNewRequest() {
+ const newRequestView = document.getElementById('new-request-view');
+ if (newRequestView) {
+ newRequestView.style.display = 'none';
+ // Optionnel : supprimer la vue du DOM
+ newRequestView.remove();
+ }
+}
+
+// N'oubliez pas d'exposer la fonction globalement
+window.closeNewRequest = closeNewRequest;
+
+document.addEventListener('DOMContentLoaded', function() {
+ const newRequestBtn = document.getElementById('newRequestBtn');
+ if (newRequestBtn) {
+ newRequestBtn.addEventListener('click', () => {
+ // Provide default/empty RequestParams when clicked
+ newRequest({
+ processId: 0,
+ processName: '',
+ roleId: 0,
+ roleName: '',
+ documentId: 0,
+ documentName: ''
+ });
+ });
+ }
+});
+
+function submitDocumentRequest(documentId: number) {
+ const createdAt = (document.getElementById('createdAt') as HTMLInputElement)?.value || '';
+ const deadline = (document.getElementById('deadline') as HTMLInputElement)?.value || '';
+ const visibility = (document.getElementById('visibility') as HTMLSelectElement)?.value || '';
+ const description = (document.getElementById('description') as HTMLTextAreaElement)?.value || '';
+
+ const selectedMembers = Array.from(
+ document.querySelectorAll('input[name="selected-members"]:checked')
+ ).map(checkbox => (checkbox as HTMLInputElement).value);
+
+ if (!createdAt || !deadline || selectedMembers.length === 0) {
+ showAlert('Please fill in all required fields and select at least one member.');
+ return;
+ }
+
+ console.log('Document submission:', {
+ documentId,
+ createdAt,
+ deadline,
+ visibility,
+ description,
+ selectedMembers
+ });
+
+ showAlert('Document request submitted successfully!');
+ closeNewRequest();
+}
+
+window.submitDocumentRequest = submitDocumentRequest;
+
+// Define allMembers using membersMock
+const allMembers = membersMock.map(member => ({
+ id: member.id,
+ name: member.name,
+ roleName: 'Default Role' // Add role information if available in your membersMock
+}));
+
+const addMembersBtn = document.getElementById('addMembersBtn');
+if (addMembersBtn) {
+ addMembersBtn.addEventListener('click', function() {
+ const selectedMembers: string[] = []; // Assuming member IDs are strings
+
+ // Logic to display a list of members to select
+ const membersToSelect = allMembers.map(member => `
+
+
+ ${member.name} (${member.roleName})
+
+ `).join('');
+
+ // Create a modal for member selection
+ const modalContent = `
+
+
Select Members
+
${membersToSelect}
+
Confirm
+
+ `;
+
+ // Append the modal to the body
+ document.body.insertAdjacentHTML('beforeend', modalContent);
+
+ // Add event for the confirmation button
+ const confirmBtn = document.getElementById('confirmSelectionBtn');
+ if (confirmBtn) {
+ confirmBtn.addEventListener('click', function() {
+ // Retrieve selected members
+ const selectedCheckboxes = document.querySelectorAll('.modal input[type="checkbox"]:checked');
+ selectedCheckboxes.forEach((checkbox: Element) => {
+ selectedMembers.push((checkbox as HTMLInputElement).value);
+ });
+
+ // Add selected members to the list
+ const membersList = document.getElementById('members-list');
+ if (membersList) {
+ selectedMembers.forEach(memberId => {
+ const member = allMembers.find(m => m.id === memberId);
+ if (member) {
+ membersList.insertAdjacentHTML('beforeend', `${member.name} (${member.roleName})
`);
+ }
+ });
+ }
+
+ // Close the modal
+ document.querySelector('.modal')?.remove();
+ });
+ }
+ });
+}
+
+// Fonction d'initialisation pour le router
+export function initSignature() {
+ // Réinitialiser l'affichage
+ const groupList = document.getElementById('group-list');
+ if (groupList) {
+ groupList.innerHTML = ''; // Nettoyer la liste existante
+ }
+
+ // Recharger les messages depuis le store
+ messagesMock = messageStore.getMessages();
+ if (messagesMock.length === 0) {
+ messageStore.setMessages(initialMessagesMock);
+ messagesMock = messageStore.getMessages();
+ }
+
+ // Réinitialiser l'interface
+ updateCurrentUserDisplay();
+ loadGroupList();
+
+ // Si un membre était sélectionné, recharger son chat
+ if (selectedMemberId) {
+ loadMemberChat(selectedMemberId);
+ }
+
+ // Event listeners
+ document.addEventListener('click', (event) => {
+ const userList = document.getElementById('userList');
+ const userSwitchBtn = document.getElementById('userSwitchBtn');
+ if (userSwitchBtn && userList && !userSwitchBtn.contains(event.target as Node) && !userList.contains(event.target as Node)) {
+ userList.classList.remove('show');
+ }
+ });
+
+ // Réattacher les event listeners pour les messages
+ const sendBtn = document.getElementById('send-button');
+ if (sendBtn) sendBtn.onclick = sendMessage;
+
+ const messageInput = document.getElementById('message-input');
+ if (messageInput) {
+ messageInput.addEventListener('keydown', function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ sendMessage();
+ }
+ });
+ }
+}
+
+
+
+
+
diff --git a/src/utils/messageMock.ts b/src/utils/messageMock.ts
new file mode 100644
index 0000000..0619b53
--- /dev/null
+++ b/src/utils/messageMock.ts
@@ -0,0 +1,53 @@
+import { messagesMock as initialMessagesMock } from '../mocks/mock-signature/messagesMock.js';
+
+// Store singleton for messages
+class MessageStore {
+ private readonly STORAGE_KEY = 'chat_messages';
+ private messages: any[] = [];
+
+ constructor() {
+ this.messages = this.loadFromLocalStorage() || [];
+ }
+
+ private 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: any[]) {
+ this.messages = messages;
+ this.saveToLocalStorage();
+ }
+
+ private saveToLocalStorage() {
+ try {
+ localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.messages));
+ } catch (error) {
+ console.error('Error saving messages:', error);
+ }
+ }
+
+ addMessage(memberId: string | number, message: any) {
+ 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();
+ }
+}
+
+export const messageStore = new MessageStore();
\ No newline at end of file
diff --git a/src/utils/notification.store.ts b/src/utils/notification.store.ts
new file mode 100644
index 0000000..8b1002c
--- /dev/null
+++ b/src/utils/notification.store.ts
@@ -0,0 +1,96 @@
+interface INotification {
+ id: number;
+ title: string;
+ description: string;
+ time?: string;
+ memberId?: string;
+}
+
+class NotificationStore {
+ private static instance: NotificationStore;
+ private notifications: INotification[] = [];
+
+ private constructor() {
+ this.loadFromLocalStorage();
+ }
+
+ static getInstance(): NotificationStore {
+ if (!NotificationStore.instance) {
+ NotificationStore.instance = new NotificationStore();
+ }
+ return NotificationStore.instance;
+ }
+
+ addNotification(notification: INotification) {
+ this.notifications.push(notification);
+ this.saveToLocalStorage();
+ this.updateUI();
+ }
+
+ removeNotification(index: number) {
+ this.notifications.splice(index, 1);
+ this.saveToLocalStorage();
+ this.updateUI();
+ }
+
+ getNotifications(): INotification[] {
+ return this.notifications;
+ }
+
+ private saveToLocalStorage() {
+ localStorage.setItem('notifications', JSON.stringify(this.notifications));
+ }
+
+ private loadFromLocalStorage() {
+ const stored = localStorage.getItem('notifications');
+ if (stored) {
+ this.notifications = JSON.parse(stored);
+ }
+ }
+
+ private updateUI() {
+ const badge = document.querySelector('.notification-badge') as HTMLElement;
+ const board = document.querySelector('.notification-board') as HTMLElement;
+
+ if (badge) {
+ badge.textContent = this.notifications.length.toString();
+ badge.style.display = this.notifications.length > 0 ? 'block' : 'none';
+ }
+
+ if (board) {
+ this.renderNotificationBoard(board);
+ }
+ }
+
+ private renderNotificationBoard(board: HTMLElement) {
+ board.innerHTML = '';
+
+ if (this.notifications.length === 0) {
+ board.innerHTML = 'No notifications available
';
+ return;
+ }
+
+ this.notifications.forEach((notif, index) => {
+ const notifElement = document.createElement('div');
+ notifElement.className = 'notification-item';
+ notifElement.innerHTML = `
+ ${notif.title}
+ ${notif.description}
+ ${notif.time ? `${notif.time}
` : ''}
+ `;
+ notifElement.onclick = () => {
+ if (notif.memberId) {
+ window.loadMemberChat(notif.memberId);
+ }
+ this.removeNotification(index);
+ };
+ board.appendChild(notifElement);
+ });
+ }
+
+ public refreshNotifications() {
+ this.updateUI();
+ }
+}
+
+export const notificationStore = NotificationStore.getInstance();
\ No newline at end of file