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 @@ +
+ + +
\ 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.

+
+ + +
+ `; + + 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 = ` + + `; +} + +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
+
+ + + + + + + + + +
Process NameRoleNotifications
+
+ `; + + // 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} +
+ +
+ `; + + 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 = ` +
+
+ +
+

${contract.title}

+
Date: ${contract.date}
+
+ +
+ Parties involved: +
    + ${contract.parties.map(party => `
  • ${party}
  • `).join('')} +
+
+ +
+ Terms and Conditions: +
    + ${contract.terms.map(term => `
  • ${term}
  • `).join('')} +
+
+ +
+ Details: +

${contract.content}

+
+
+
+ `; + + 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 = ` +
Pairing
+
+ + + + + + + + + + +
SP AddressDevice NameSP Emojis
+
+ +
+
+ `; + + // 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 = ` +
Wallet
+
+ + + + + + + + + +
LabelWalletType
+
+ +
+
+ `; + + 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 = ` +
Data
+
+ + + + + + + + + + + + +
NameVisibilityRoleDurationLegalContract
+
+ `; + + 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 = ` + + `; + + +} + +// 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 = ` + + `; + } +} + +// 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 = ` + + `; + } +} + +// 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 = ` +
+

${group.name}

+
+
+
+
+
+

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 ? `⚠️ ${document.name}` : document.name}

+ ${document.visibility} +
+
+ ${!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 = ` +
+

${role.name} Documents

+
+ +
+
+
+
+

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 ? `⚠️ ${document.name}` : document.name}

+ ${document.visibility} +
+
+ ${!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

+ + `} +
+
+ `; + }).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 => ` +
+ + +
+ `).join(''); + + // Create a modal for member selection + const modalContent = ` + + `; + + // 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