From 2124388cc5662a841b9e6b23201632c6bd5689e8 Mon Sep 17 00:00:00 2001 From: AnisHADJARAB Date: Tue, 17 Dec 2024 08:28:39 +0000 Subject: [PATCH] link diffs to validation modal and show and prettify --- src/4nk.css | 1569 ++++---- src/components/header/header.ts | 63 +- .../profile-header/profile-header.html | 24 +- .../qrcode-scanner-component.ts | 147 +- .../validation-modal/validation-modal.css | 140 +- .../validation-modal/validation-modal.html | 64 +- .../validation-modal/validation-modal.ts | 65 +- src/decs.d.ts | 20 +- src/interface/groupInterface.ts | 44 +- src/interface/memberInterface.ts | 14 +- src/main.ts | 68 +- src/mocks/mock-account/constAccountMock.ts | 600 ++- .../mock-account/interfacesAccountMock.ts | 90 +- src/mocks/mock-chat/groupsMock.js | 92 +- src/mocks/mock-chat/messagesMock.js | 129 +- src/mocks/mock-signature/groupsMock.js | 931 ++--- src/mocks/mock-signature/membersMocks.js | 210 +- src/mocks/mock-signature/messagesMock.ts | 129 +- src/models/notification.model.ts | 16 +- src/models/signature.models.ts | 118 +- src/pages/account/account-component.ts | 125 +- src/pages/account/account.html | 190 +- src/pages/account/account.ts | 2528 ++++++------ src/pages/chat/chat-component.ts | 118 +- src/pages/chat/chat.html | 101 +- src/pages/chat/chat.ts | 1063 +++-- src/pages/home/home-component.ts | 97 +- src/pages/home/home.html | 2 +- src/pages/home/home.ts | 12 +- .../process-element/process-component.ts | 101 +- src/pages/process/process-list-component.ts | 97 +- src/pages/process/process.ts | 16 +- src/pages/signature/signature-component.ts | 118 +- src/pages/signature/signature.html | 99 +- src/pages/signature/signature.ts | 3427 +++++++++-------- src/router.ts | 131 +- src/service-workers/cache.worker.js | 28 +- src/service-workers/database.worker.js | 50 +- src/services/database.service.ts | 21 +- src/services/modal.service.ts | 30 +- src/services/service.ts | 32 +- src/utils/document.utils.ts | 8 +- src/utils/html.utils.ts | 6 +- src/utils/messageMock.ts | 106 +- src/utils/notification.store.ts | 192 +- src/utils/sp-address.utils.ts | 8 +- 46 files changed, 6581 insertions(+), 6658 deletions(-) diff --git a/src/4nk.css b/src/4nk.css index 0657acf..d580e13 100644 --- a/src/4nk.css +++ b/src/4nk.css @@ -1,788 +1,781 @@ -:host { - --primary-color - : #3A506B; - /* Bleu métallique */ - --secondary-color - : #B0BEC5; - /* Gris acier */ - --accent-color - : #D68C45; - /* Cuivre */ - font-family: Arial, sans-serif; - height: 100vh; - font-size: 16px; -} -body { - font-family: Arial, sans-serif; - margin: 0; - padding: 0; - background-color: #f4f4f4; - background-image: url(../assets/bgd.webp); - background-repeat:no-repeat; - background-size: cover; - background-blend-mode :soft-light; - height: 100vh; - } - .message { - margin: 30px 0; - font-size: 14px; - overflow-wrap: anywhere; - } - - .message strong{ - font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif; - font-size: 20px; - } - - /** Modal Css */ - .modal { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - justify-content: center; - align-items: center; - z-index: 3; - } - - .modal-content { - width: 55%; - height: 30%; - background-color: white; - border-radius: 4px; - padding: 20px; - text-align: center; - display: flex; - flex-direction: column; - align-items: center; - } - - .modal-title { - margin: 0; - padding-bottom: 8px; - width: 100%; - font-size: 0.9rem; - border-bottom: 1px solid #ccc; - } - - .confirmation-box { - /* margin-top: 20px; */ - align-content: center; - width: 70%; - height: 20%; - /* padding: 20px; */ - font-size: 1.5em; - color: #333333; - top: 5%; - position: relative; - } - - - .nav-wrapper { - position: fixed; - background: radial-gradient(circle, white, var(--primary-color)); - /* background-color: #CFD8DC; */ - display: flex; - justify-content: flex-end; - 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); - - .nav-right-icons { - display: flex; - .notification-container { - position: relative; - display: inline-block; - } - .notification-bell, .burger-menu { - z-index: 3; - height: 20px; - width: 20px; - margin-right: 1rem; - } - .notification-badge { - position: absolute; - top: -.7rem; - left: -.8rem; - background-color: red; - color: white; - border-radius: 50%; - padding: 2.5px 6px; - font-size: 0.8rem; - font-weight: bold; - } - } - .notification-board { - position: absolute; - width: 20rem; - min-height: 8rem; - background-color: white; - right: 0.5rem; - display: none; - border-radius: 4px; - text-align: center; - display: flex; - flex-direction: column; - align-items: center; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - display: none; - - .notification-element { - padding: .8rem 0; - width: 100%; - &:hover { - background-color: rgba(26, 28, 24, .08); - } - } - .notification-element:not(:last-child) { - border-bottom: 1px solid; - } - } - } - - .brand-logo { - height: 100%; - width: 100vw; - align-content: center; - position: relative; - display: flex; - position: absolute; - align-items: center; - justify-content: center; - text-align: center; - font-size: 1.5em; - font-weight: bold; - } - - .container { - text-align: center; - display: grid; - height: 100vh; - grid-template-columns: repeat(7, 1fr); - gap: 10px; - grid-auto-rows: 10vh 15vh 1fr; - } - .title-container { - grid-column: 2 / 7; - grid-row: 2; - } - .page-container { - grid-column: 2 / 7; - grid-row: 3 ; - justify-content: center; - display: flex; - padding: 1rem; - box-sizing: border-box; - max-height: 60vh; - } - - h1 { - font-size: 2em; - margin: 20px 0; - } - @media only screen and (min-width: 600px) { - .tab-container { - display: none; - } - .page-container { - display: flex; - align-items: center; - } - .process-container { - grid-column: 3 / 6; - grid-row: 3 ; - - .card { - min-width: 40vw; - } - } - .separator { - width: 2px; - background-color: #78909C; - height: 80%; - margin: 0 0.5em; - } - .tab-content { - display: flex; - flex-direction: column; - justify-content: space-evenly; - align-items: center; - height: 80%; - } - } - - @media only screen and (max-width: 600px) { - .process-container { - grid-column: 2 / 7; - grid-row: 3 ; - } - .container { - grid-auto-rows: 10vh 15vh 15vh 1fr; - } - .tab-container { - grid-column: 1 / 8; - grid-row: 3; - } - .page-container { - grid-column: 2 / 7; - grid-row: 4 ; - } - .separator { - display: none; - } - .tabs { - display: flex; - flex-grow: 1; - overflow: hidden; - z-index: 1; - border-bottom-style: solid; - border-bottom-width: 1px; - border-bottom-color: #E0E4D6; - } - - .tab { - flex: 1; - text-align: center; - padding: 10px 0; - cursor: pointer; - font-size: 1rem; - color: #6200ea; - &:hover { - background-color: rgba(26, 28, 24, .08); - } - } - .tab.active { - border-bottom: 2px solid #6200ea; - font-weight: bold; - } - - .card.tab-content { - display: none; - } - - .tab-content.active { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - height: 80%; - } - .modal-content { - width: 80%; - height: 20%; - } - } - - .qr-code { - display: flex; - justify-content: center; - align-items: center; - height: 200px; - } - - .emoji-display { - font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif; - font-size: 20px; - - } - - #emoji-display-2{ - margin-top: 30px; - font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif; - font-size: 20px; - } - - #okButton{ - margin-bottom: 2em; - cursor: pointer; - background-color: #D0D0D7; - color: white; - border-style: none; - border-radius: 5px; - color: #000; - padding: 2px; - margin-top: 10px; - } - - .pairing-request { - font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif; - font-size: 14px; - margin-top: 0px; - } - - .sp-address-btn { - margin-bottom: 2em; - cursor: pointer; - background-color: #D0D0D7; - color: white; - border-style: none; - border-radius: 5px; - color: #000; - padding: 2px; - - } - - .camera-card { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - /* height: 200px; */ - } - - .btn { - display: inline-block; - padding: 10px 20px; - background-color: var(--primary-color); - color: white; - text-align: center; - border-radius: 5px; - cursor: pointer; - text-decoration: none; - } - - .btn:hover { - background-color: #3700b3; - } - - - .card { - min-width: 300px; - border: 1px solid #e0e0e0; - border-radius: 8px; - background-color: white; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - box-sizing: border-box; - overflow: hidden; - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - min-height: 40vh; - max-height: 60vh; - justify-content: flex-start; - padding: 1rem; - overflow-y: auto; - - } - - .card-content { - flex-grow: 1; - flex-direction: column; - display: flex; - justify-content: flex-start; - align-items: center; - text-align: left; - font-size: .8em; - position: relative; - left: 2vw; - width: 90%; - .process-title { - font-weight: bold; - padding: 1rem 0; - } - .process-element { - padding: .4rem 0; - &:hover { - background-color: rgba(26, 28, 24, .08); - } - &.selected { - background-color: rgba(26, 28, 24, .08); - } - } - } - - .card-description { - padding: 20px; - font-size: 1rem; - color: #333; - width: 90%; - height: 50px; - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 0px; - } - - - .card-action { - width: 100%; - } - - .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; - } - - .qr-code-scanner { - display: none; - } - - - /* QR READER */ - #qr-reader div { - position: inherit; - } - - #qr-reader div img{ - top: 15px ; - right: 25px; - margin-top: 5px; - } - - - /* INPUT CSS **/ - .input-container { - position: relative; - width: 100%; - background-color: #ECEFF1; - } - - .input-field { - width: 36vw; - padding: 10px 0; - font-size: 1rem; - border: none; - border-bottom: 1px solid #ccc; - outline: none; - background: transparent; - transition: border-color 0.3s; - } - - .input-field:focus { - border-bottom: 2px solid #6200ea; - } - - .input-label { - position: absolute; - margin-top: -0.5em; - top: 0; - left: 0; - padding: 10px 0; - font-size: 1rem; - color: #999; - pointer-events: none; - transition: transform 0.3s, color 0.3s, font-size 0.3s; - } - - .input-field:focus + .input-label, - .input-field:not(:placeholder-shown) + .input-label { - transform: translateY(-20px); - font-size: 0.8em; - color: #6200ea; - } - - .input-underline { - position: absolute; - bottom: 0; - left: 50%; - width: 0; - height: 2px; - background-color: #6200ea; - transition: width 0.3s, left 0.3s; - } - - .input-field:focus ~ .input-underline { - width: 100%; - left: 0; - } - - .dropdown-content { - position: absolute; - flex-direction: column; - top: 100%; - left: 0; - width: 100%; - max-height: 150px; - overflow-y: auto; - border: 1px solid #ccc; - border-radius: 4px; - background-color: white; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - display: none; - z-index: 1; - } - - .dropdown-content span { - padding: 10px; - cursor: pointer; - list-style: none; - } - - .dropdown-content span:hover { - background-color: #f0f0f0; - } - - - - - /** AUTOCOMPLETE **/ - -select[data-multi-select-plugin] { - display: none !important; -} - -.multi-select-component { - width: 36vw; - padding: 5px 0; - font-size: 1rem; - border: none; - border-bottom: 1px solid #ccc; - outline: none; - background: transparent; - display: flex; - flex-direction: row; - height: auto; - width: 100%; - -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; - transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; -} - -.autocomplete-list { - border-radius: 4px 0px 0px 4px; -} - -.multi-select-component:focus-within { - box-shadow: inset 0px 0px 0px 2px #78ABFE; -} - -.multi-select-component .btn-group { - display: none !important; -} - -.multiselect-native-select .multiselect-container { - width: 100%; -} - -.selected-processes { - background-color: white; - padding: 0.4em; -} - -.selected-wrapper { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - display: inline-block; - border: 1px solid #d9d9d9; - background-color: #ededed; - white-space: nowrap; - margin: 1px 5px 5px 0; - height: 22px; - vertical-align: top; - cursor: default; -} - -.selected-wrapper .selected-label { - max-width: 514px; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - padding-left: 4px; - vertical-align: top; -} - -.selected-wrapper .selected-close { - display: inline-block; - text-decoration: none; - font-size: 14px; - line-height: 1.49rem; - margin-left: 5px; - padding-bottom: 10px; - height: 100%; - vertical-align: top; - padding-right: 4px; - opacity: 0.2; - color: #000; - text-shadow: 0 1px 0 #fff; - font-weight: 700; -} - -.search-container { - display: flex; - flex-direction: row; -} - -.search-container .selected-input { - background: none; - border: 0; - height: 20px; - width: 60px; - padding: 0; - margin-bottom: 6px; - -webkit-box-shadow: none; - box-shadow: none; -} - -.search-container .selected-input:focus { - outline: none; -} - -.dropdown-icon.active { - transform: rotateX(180deg) -} - -.search-container .dropdown-icon { - display: inline-block; - padding: 10px 5px; - position: absolute; - top: 5px; - right: 5px; - width: 10px; - height: 10px; - border: 0 !important; - /* needed */ - -webkit-appearance: none; - -moz-appearance: none; - /* SVG background image */ - background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2012%2012%22%3E%3Ctitle%3Edown-arrow%3C%2Ftitle%3E%3Cg%20fill%3D%22%23818181%22%3E%3Cpath%20d%3D%22M10.293%2C3.293%2C6%2C7.586%2C1.707%2C3.293A1%2C1%2C0%2C0%2C0%2C.293%2C4.707l5%2C5a1%2C1%2C0%2C0%2C0%2C1.414%2C0l5-5a1%2C1%2C0%2C1%2C0-1.414-1.414Z%22%20fill%3D%22%23818181%22%3E%3C%2Fpath%3E%3C%2Fg%3E%3C%2Fsvg%3E"); - background-position: center; - background-size: 10px; - background-repeat: no-repeat; -} - -.search-container ul { - position: absolute; - list-style: none; - padding: 0; - z-index: 3; - margin-top: 29px; - width: 100%; - right: 0px; - background: #fff; - border: 1px solid #ccc; - border-top: none; - border-bottom: none; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} - -.search-container ul :focus { - outline: none; -} - -.search-container ul li { - display: block; - text-align: left; - padding: 8px 29px 2px 12px; - border-bottom: 1px solid #ccc; - font-size: 14px; - min-height: 31px; -} - -.search-container ul li:first-child { - border-top: 1px solid #ccc; - border-radius: 4px 0px 0 0; -} - -.search-container ul li:last-child { - border-radius: 4px 0px 0 0; -} - - -.search-container ul li:hover.not-cursor { - cursor: default; -} - -.search-container ul li:hover { - color: #333; - background-color: #f0f0f0; - ; - border-color: #adadad; - cursor: pointer; -} - -/* Adding scrool to select options */ -.autocomplete-list { - max-height: 130px; - overflow-y: auto; -} - - - -/**************************************** Process page card ******************************************************/ -.process-card { - min-width: 300px; - border: 1px solid #e0e0e0; - border-radius: 8px; - background-color: white; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - overflow: hidden; - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - min-height: 40vh; - max-height: 60vh; - justify-content: space-between; - padding: 1rem; - overflow-y: auto; - -} - -.process-card-content { - text-align: left; - font-size: .8em; - position: relative; - left: 2vw; - width: 90%; - .process-title { - font-weight: bold; - padding: 1rem 0; - } - .process-element { - padding: .4rem 0; - &:hover { - background-color: rgba(26, 28, 24, .08); - } - &.selected { - background-color: rgba(26, 28, 24, .08); - } - } - .selected-process-zone { - background-color: rgba(26, 28, 24, .08); - } -} - -.process-card-description { - padding: 20px; - font-size: 1rem; - color: #333; - width: 90%; -} - - -.process-card-action { - width: 100%; -} \ No newline at end of file +:host { + --primary-color: #3a506b; + /* Bleu métallique */ + --secondary-color: #b0bec5; + /* Gris acier */ + --accent-color: #d68c45; + /* Cuivre */ + font-family: Arial, sans-serif; + height: 100vh; + font-size: 16px; +} +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + background-color: #f4f4f4; + background-image: url(../assets/bgd.webp); + background-repeat: no-repeat; + background-size: cover; + background-blend-mode: soft-light; + height: 100vh; +} +.message { + margin: 30px 0; + font-size: 14px; + overflow-wrap: anywhere; +} + +.message strong { + font-family: 'Segoe UI Emoji', 'Noto Color Emoji', 'Apple Color Emoji', sans-serif; + font-size: 20px; +} + +/** Modal Css */ +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + justify-content: center; + align-items: center; + z-index: 3; +} + +.modal-content { + width: 55%; + height: 30%; + background-color: white; + border-radius: 4px; + padding: 20px; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; +} + +.modal-title { + margin: 0; + padding-bottom: 8px; + width: 100%; + font-size: 0.9rem; + border-bottom: 1px solid #ccc; +} + +.confirmation-box { + /* margin-top: 20px; */ + align-content: center; + width: 70%; + height: 20%; + /* padding: 20px; */ + font-size: 1.5em; + color: #333333; + top: 5%; + position: relative; +} + +.nav-wrapper { + position: fixed; + background: radial-gradient(circle, white, var(--primary-color)); + /* background-color: #CFD8DC; */ + display: flex; + justify-content: flex-end; + align-items: center; + color: #37474f; + height: 9vh; + width: 100vw; + left: 0; + top: 0; + box-shadow: + 0px 8px 10px -5px rgba(0, 0, 0, 0.2), + 0px 16px 24px 2px rgba(0, 0, 0, 0.14), + 0px 6px 30px 5px rgba(0, 0, 0, 0.12); + + .nav-right-icons { + display: flex; + .notification-container { + position: relative; + display: inline-block; + } + .notification-bell, + .burger-menu { + z-index: 3; + height: 20px; + width: 20px; + margin-right: 1rem; + } + .notification-badge { + position: absolute; + top: -0.7rem; + left: -0.8rem; + background-color: red; + color: white; + border-radius: 50%; + padding: 2.5px 6px; + font-size: 0.8rem; + font-weight: bold; + } + } + .notification-board { + position: absolute; + width: 20rem; + min-height: 8rem; + background-color: white; + right: 0.5rem; + display: none; + border-radius: 4px; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + display: none; + + .notification-element { + padding: 0.8rem 0; + width: 100%; + &:hover { + background-color: rgba(26, 28, 24, 0.08); + } + } + .notification-element:not(:last-child) { + border-bottom: 1px solid; + } + } +} + +.brand-logo { + height: 100%; + width: 100vw; + align-content: center; + position: relative; + display: flex; + position: absolute; + align-items: center; + justify-content: center; + text-align: center; + font-size: 1.5em; + font-weight: bold; +} + +.container { + text-align: center; + display: grid; + height: 100vh; + grid-template-columns: repeat(7, 1fr); + gap: 10px; + grid-auto-rows: 10vh 15vh 1fr; +} +.title-container { + grid-column: 2 / 7; + grid-row: 2; +} +.page-container { + grid-column: 2 / 7; + grid-row: 3; + justify-content: center; + display: flex; + padding: 1rem; + box-sizing: border-box; + max-height: 60vh; +} + +h1 { + font-size: 2em; + margin: 20px 0; +} +@media only screen and (min-width: 600px) { + .tab-container { + display: none; + } + .page-container { + display: flex; + align-items: center; + } + .process-container { + grid-column: 3 / 6; + grid-row: 3; + + .card { + min-width: 40vw; + } + } + .separator { + width: 2px; + background-color: #78909c; + height: 80%; + margin: 0 0.5em; + } + .tab-content { + display: flex; + flex-direction: column; + justify-content: space-evenly; + align-items: center; + height: 80%; + } +} + +@media only screen and (max-width: 600px) { + .process-container { + grid-column: 2 / 7; + grid-row: 3; + } + .container { + grid-auto-rows: 10vh 15vh 15vh 1fr; + } + .tab-container { + grid-column: 1 / 8; + grid-row: 3; + } + .page-container { + grid-column: 2 / 7; + grid-row: 4; + } + .separator { + display: none; + } + .tabs { + display: flex; + flex-grow: 1; + overflow: hidden; + z-index: 1; + border-bottom-style: solid; + border-bottom-width: 1px; + border-bottom-color: #e0e4d6; + } + + .tab { + flex: 1; + text-align: center; + padding: 10px 0; + cursor: pointer; + font-size: 1rem; + color: #6200ea; + &:hover { + background-color: rgba(26, 28, 24, 0.08); + } + } + .tab.active { + border-bottom: 2px solid #6200ea; + font-weight: bold; + } + + .card.tab-content { + display: none; + } + + .tab-content.active { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 80%; + } + .modal-content { + width: 80%; + height: 20%; + } +} + +.qr-code { + display: flex; + justify-content: center; + align-items: center; + height: 200px; +} + +.emoji-display { + font-family: 'Segoe UI Emoji', 'Noto Color Emoji', 'Apple Color Emoji', sans-serif; + font-size: 20px; +} + +#emoji-display-2 { + margin-top: 30px; + font-family: 'Segoe UI Emoji', 'Noto Color Emoji', 'Apple Color Emoji', sans-serif; + font-size: 20px; +} + +#okButton { + margin-bottom: 2em; + cursor: pointer; + background-color: #d0d0d7; + color: white; + border-style: none; + border-radius: 5px; + color: #000; + padding: 2px; + margin-top: 10px; +} + +.pairing-request { + font-family: 'Segoe UI Emoji', 'Noto Color Emoji', 'Apple Color Emoji', sans-serif; + font-size: 14px; + margin-top: 0px; +} + +.sp-address-btn { + margin-bottom: 2em; + cursor: pointer; + background-color: #d0d0d7; + color: white; + border-style: none; + border-radius: 5px; + color: #000; + padding: 2px; +} + +.camera-card { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + /* height: 200px; */ +} + +.btn { + display: inline-block; + padding: 10px 20px; + background-color: var(--primary-color); + color: white; + text-align: center; + border-radius: 5px; + cursor: pointer; + text-decoration: none; +} + +.btn:hover { + background-color: #3700b3; +} + +.card { + min-width: 300px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: white; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + box-sizing: border-box; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + min-height: 40vh; + max-height: 60vh; + justify-content: flex-start; + padding: 1rem; + overflow-y: auto; +} + +.card-content { + flex-grow: 1; + flex-direction: column; + display: flex; + justify-content: flex-start; + align-items: center; + text-align: left; + font-size: 0.8em; + position: relative; + left: 2vw; + width: 90%; + .process-title { + font-weight: bold; + padding: 1rem 0; + } + .process-element { + padding: 0.4rem 0; + &:hover { + background-color: rgba(26, 28, 24, 0.08); + } + &.selected { + background-color: rgba(26, 28, 24, 0.08); + } + } +} + +.card-description { + padding: 20px; + font-size: 1rem; + color: #333; + width: 90%; + height: 50px; + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 0px; +} + +.card-action { + width: 100%; +} + +.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, 0.08); + } +} + +.menu-content a:last-child { + border-bottom: none; +} + +.qr-code-scanner { + display: none; +} + +/* QR READER */ +#qr-reader div { + position: inherit; +} + +#qr-reader div img { + top: 15px; + right: 25px; + margin-top: 5px; +} + +/* INPUT CSS **/ +.input-container { + position: relative; + width: 100%; + background-color: #eceff1; +} + +.input-field { + width: 36vw; + padding: 10px 0; + font-size: 1rem; + border: none; + border-bottom: 1px solid #ccc; + outline: none; + background: transparent; + transition: border-color 0.3s; +} + +.input-field:focus { + border-bottom: 2px solid #6200ea; +} + +.input-label { + position: absolute; + margin-top: -0.5em; + top: 0; + left: 0; + padding: 10px 0; + font-size: 1rem; + color: #999; + pointer-events: none; + transition: + transform 0.3s, + color 0.3s, + font-size 0.3s; +} + +.input-field:focus + .input-label, +.input-field:not(:placeholder-shown) + .input-label { + transform: translateY(-20px); + font-size: 0.8em; + color: #6200ea; +} + +.input-underline { + position: absolute; + bottom: 0; + left: 50%; + width: 0; + height: 2px; + background-color: #6200ea; + transition: + width 0.3s, + left 0.3s; +} + +.input-field:focus ~ .input-underline { + width: 100%; + left: 0; +} + +.dropdown-content { + position: absolute; + flex-direction: column; + top: 100%; + left: 0; + width: 100%; + max-height: 150px; + overflow-y: auto; + border: 1px solid #ccc; + border-radius: 4px; + background-color: white; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + display: none; + z-index: 1; +} + +.dropdown-content span { + padding: 10px; + cursor: pointer; + list-style: none; +} + +.dropdown-content span:hover { + background-color: #f0f0f0; +} + +/** AUTOCOMPLETE **/ + +select[data-multi-select-plugin] { + display: none !important; +} + +.multi-select-component { + width: 36vw; + padding: 5px 0; + font-size: 1rem; + border: none; + border-bottom: 1px solid #ccc; + outline: none; + background: transparent; + display: flex; + flex-direction: row; + height: auto; + width: 100%; + -o-transition: + border-color ease-in-out 0.15s, + box-shadow ease-in-out 0.15s; + transition: + border-color ease-in-out 0.15s, + box-shadow ease-in-out 0.15s; +} + +.autocomplete-list { + border-radius: 4px 0px 0px 4px; +} + +.multi-select-component:focus-within { + box-shadow: inset 0px 0px 0px 2px #78abfe; +} + +.multi-select-component .btn-group { + display: none !important; +} + +.multiselect-native-select .multiselect-container { + width: 100%; +} + +.selected-processes { + background-color: white; + padding: 0.4em; +} + +.selected-wrapper { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + display: inline-block; + border: 1px solid #d9d9d9; + background-color: #ededed; + white-space: nowrap; + margin: 1px 5px 5px 0; + height: 22px; + vertical-align: top; + cursor: default; +} + +.selected-wrapper .selected-label { + max-width: 514px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + padding-left: 4px; + vertical-align: top; +} + +.selected-wrapper .selected-close { + display: inline-block; + text-decoration: none; + font-size: 14px; + line-height: 1.49rem; + margin-left: 5px; + padding-bottom: 10px; + height: 100%; + vertical-align: top; + padding-right: 4px; + opacity: 0.2; + color: #000; + text-shadow: 0 1px 0 #fff; + font-weight: 700; +} + +.search-container { + display: flex; + flex-direction: row; +} + +.search-container .selected-input { + background: none; + border: 0; + height: 20px; + width: 60px; + padding: 0; + margin-bottom: 6px; + -webkit-box-shadow: none; + box-shadow: none; +} + +.search-container .selected-input:focus { + outline: none; +} + +.dropdown-icon.active { + transform: rotateX(180deg); +} + +.search-container .dropdown-icon { + display: inline-block; + padding: 10px 5px; + position: absolute; + top: 5px; + right: 5px; + width: 10px; + height: 10px; + border: 0 !important; + /* needed */ + -webkit-appearance: none; + -moz-appearance: none; + /* SVG background image */ + background-image: url('data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2012%2012%22%3E%3Ctitle%3Edown-arrow%3C%2Ftitle%3E%3Cg%20fill%3D%22%23818181%22%3E%3Cpath%20d%3D%22M10.293%2C3.293%2C6%2C7.586%2C1.707%2C3.293A1%2C1%2C0%2C0%2C0%2C.293%2C4.707l5%2C5a1%2C1%2C0%2C0%2C0%2C1.414%2C0l5-5a1%2C1%2C0%2C1%2C0-1.414-1.414Z%22%20fill%3D%22%23818181%22%3E%3C%2Fpath%3E%3C%2Fg%3E%3C%2Fsvg%3E'); + background-position: center; + background-size: 10px; + background-repeat: no-repeat; +} + +.search-container ul { + position: absolute; + list-style: none; + padding: 0; + z-index: 3; + margin-top: 29px; + width: 100%; + right: 0px; + background: #fff; + border: 1px solid #ccc; + border-top: none; + border-bottom: none; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} + +.search-container ul :focus { + outline: none; +} + +.search-container ul li { + display: block; + text-align: left; + padding: 8px 29px 2px 12px; + border-bottom: 1px solid #ccc; + font-size: 14px; + min-height: 31px; +} + +.search-container ul li:first-child { + border-top: 1px solid #ccc; + border-radius: 4px 0px 0 0; +} + +.search-container ul li:last-child { + border-radius: 4px 0px 0 0; +} + +.search-container ul li:hover.not-cursor { + cursor: default; +} + +.search-container ul li:hover { + color: #333; + background-color: #f0f0f0; + border-color: #adadad; + cursor: pointer; +} + +/* Adding scrool to select options */ +.autocomplete-list { + max-height: 130px; + overflow-y: auto; +} + +/**************************************** Process page card ******************************************************/ +.process-card { + min-width: 300px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: white; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + min-height: 40vh; + max-height: 60vh; + justify-content: space-between; + padding: 1rem; + overflow-y: auto; +} + +.process-card-content { + text-align: left; + font-size: 0.8em; + position: relative; + left: 2vw; + width: 90%; + .process-title { + font-weight: bold; + padding: 1rem 0; + } + .process-element { + padding: 0.4rem 0; + &:hover { + background-color: rgba(26, 28, 24, 0.08); + } + &.selected { + background-color: rgba(26, 28, 24, 0.08); + } + } + .selected-process-zone { + background-color: rgba(26, 28, 24, 0.08); + } +} + +.process-card-description { + padding: 20px; + font-size: 1rem; + color: #333; + width: 90%; +} + +.process-card-action { + width: 100%; +} diff --git a/src/components/header/header.ts b/src/components/header/header.ts index 7c3fca3..ee8f096 100755 --- a/src/components/header/header.ts +++ b/src/components/header/header.ts @@ -42,12 +42,11 @@ export async function initHeader() { // Charger le profile-header const profileContainer = document.getElementById('profile-header-container'); if (profileContainer) { - const profileHeaderHtml = await fetch('/src/components/profile-header/profile-header.html') - .then(res => res.text()); - profileContainer.innerHTML = profileHeaderHtml; - - // Initialiser les données du profil - loadUserProfile(); + const profileHeaderHtml = await fetch('/src/components/profile-header/profile-header.html').then((res) => res.text()); + profileContainer.innerHTML = profileHeaderHtml; + + // Initialiser les données du profil + loadUserProfile(); } } if (currentRoute === 'home') { @@ -77,7 +76,7 @@ async function setNotification(notifications: any[]): Promise { if (notifications?.length) { badge.innerText = notifications.length.toString(); const notificationBoard = document.querySelector('.notification-board') as HTMLDivElement; - notificationBoard.querySelectorAll('.notification-element')?.forEach(elem => elem.remove()) + notificationBoard.querySelectorAll('.notification-element')?.forEach((elem) => elem.remove()); noNotifications.style.display = 'none'; for (const notif of notifications) { const notifElement = document.createElement('div'); @@ -90,9 +89,9 @@ async function setNotification(notifications: any[]): Promise { // this.addSubscription(notifElement, 'click', 'goToProcessPage') notificationBoard.appendChild(notifElement); notifElement.addEventListener('click', async () => { - const modalService = await ModalService.getInstance() - modalService.injectValidationModal(notif) - }) + const modalService = await ModalService.getInstance(); + modalService.injectValidationModal(notif); + }); } } else { noNotifications.style.display = 'block'; @@ -125,29 +124,29 @@ async function loadUserProfile() { } async function importJSON() { -const input = document.createElement('input'); -input.type = 'file'; -input.accept = '.json'; + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; -input.onchange = async (e) => { - const file = (e.target as HTMLInputElement).files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onload = async (e) => { - try { - const content = JSON.parse(e.target?.result as string); - const service = await Services.getInstance(); - await service.importJSON(content); - alert('Import réussi'); - } catch (error) { - alert('Erreur lors de l\'import: ' + error); - } - }; - reader.readAsText(file); - } -}; + input.onchange = async (e) => { + const file = (e.target as HTMLInputElement).files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = async (e) => { + try { + const content = JSON.parse(e.target?.result as string); + const service = await Services.getInstance(); + await service.importJSON(content); + alert('Import réussi'); + } catch (error) { + alert("Erreur lors de l'import: " + error); + } + }; + reader.readAsText(file); + } + }; -input.click(); + input.click(); } -(window as any).importJSON = importJSON; \ No newline at end of file +(window as any).importJSON = importJSON; diff --git a/src/components/profile-header/profile-header.html b/src/components/profile-header/profile-header.html index 51fd69d..c2800a3 100755 --- a/src/components/profile-header/profile-header.html +++ b/src/components/profile-header/profile-header.html @@ -1,12 +1,12 @@ -
- - -
\ No newline at end of file +
+ + +
diff --git a/src/components/qrcode-scanner/qrcode-scanner-component.ts b/src/components/qrcode-scanner/qrcode-scanner-component.ts index fab8055..bff3e08 100644 --- a/src/components/qrcode-scanner/qrcode-scanner-component.ts +++ b/src/components/qrcode-scanner/qrcode-scanner-component.ts @@ -1,74 +1,73 @@ -import QrScanner from 'qr-scanner'; -import Services from '../../services/service'; -import { prepareAndSendPairingTx } from '~/utils/sp-address.utils'; - - export default class QrScannerComponent extends HTMLElement { - videoElement: any; - wrapper: any - qrScanner: any - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - this.wrapper = document.createElement('div'); - this.wrapper.style.position = 'relative'; - this.wrapper.style.width = '150px'; - this.wrapper.style.height = '150px'; - - this.videoElement = document.createElement('video'); - this.videoElement.style.width = '100%'; - document.body?.append(this.wrapper); - this.wrapper.prepend(this.videoElement); - } - - connectedCallback() { - this.initializeScanner(); - } - - async initializeScanner() { - if (!this.videoElement) { - console.error('Video element not found!'); - return; - } - console.log("🚀 ~ QrScannerComponent ~ initializeScanner ~ this.videoElement:", this.videoElement) - this.qrScanner = new QrScanner(this.videoElement, result => this.onQrCodeScanned(result),{ - highlightScanRegion: true, - highlightCodeOutline: true, - }); - - try { - await QrScanner.hasCamera(); - this.qrScanner.start(); - this.videoElement.style = 'height: 200px; width: 200px' - this.shadowRoot?.appendChild(this.wrapper) - - } catch (e) { - console.error('No camera found or error starting the QR scanner', e); - } - } - - async onQrCodeScanned(result: any) { - console.log(`QR Code detected:`, result); - const data = result.data; - const scannedUrl = new URL(data); - - // Extract the 'sp_address' parameter - const spAddress = scannedUrl.searchParams.get('sp_address'); - if(spAddress) { - // Call the sendPairingTx function with the extracted sp_address - try { - await prepareAndSendPairingTx(spAddress) - } catch (e) { - console.error('Failed to pair:', e); - } - } - this.qrScanner.stop(); // if you want to stop scanning after one code is detected - } - - disconnectedCallback() { - if (this.qrScanner) { - this.qrScanner.destroy(); - } - } - } - - customElements.define('qr-scanner', QrScannerComponent); \ No newline at end of file +import QrScanner from 'qr-scanner'; +import Services from '../../services/service'; +import { prepareAndSendPairingTx } from '~/utils/sp-address.utils'; + +export default class QrScannerComponent extends HTMLElement { + videoElement: any; + wrapper: any; + qrScanner: any; + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this.wrapper = document.createElement('div'); + this.wrapper.style.position = 'relative'; + this.wrapper.style.width = '150px'; + this.wrapper.style.height = '150px'; + + this.videoElement = document.createElement('video'); + this.videoElement.style.width = '100%'; + document.body?.append(this.wrapper); + this.wrapper.prepend(this.videoElement); + } + + connectedCallback() { + this.initializeScanner(); + } + + async initializeScanner() { + if (!this.videoElement) { + console.error('Video element not found!'); + return; + } + console.log('🚀 ~ QrScannerComponent ~ initializeScanner ~ this.videoElement:', this.videoElement); + this.qrScanner = new QrScanner(this.videoElement, (result) => this.onQrCodeScanned(result), { + highlightScanRegion: true, + highlightCodeOutline: true, + }); + + try { + await QrScanner.hasCamera(); + this.qrScanner.start(); + this.videoElement.style = 'height: 200px; width: 200px'; + this.shadowRoot?.appendChild(this.wrapper); + } catch (e) { + console.error('No camera found or error starting the QR scanner', e); + } + } + + async onQrCodeScanned(result: any) { + console.log(`QR Code detected:`, result); + const data = result.data; + const scannedUrl = new URL(data); + + // Extract the 'sp_address' parameter + const spAddress = scannedUrl.searchParams.get('sp_address'); + if (spAddress) { + // Call the sendPairingTx function with the extracted sp_address + try { + await prepareAndSendPairingTx(spAddress); + } catch (e) { + console.error('Failed to pair:', e); + } + } + this.qrScanner.stop(); // if you want to stop scanning after one code is detected + } + + disconnectedCallback() { + if (this.qrScanner) { + this.qrScanner.destroy(); + } + } +} + +customElements.define('qr-scanner', QrScannerComponent); diff --git a/src/components/validation-modal/validation-modal.css b/src/components/validation-modal/validation-modal.css index fbdf842..b6e9381 100644 --- a/src/components/validation-modal/validation-modal.css +++ b/src/components/validation-modal/validation-modal.css @@ -1,70 +1,70 @@ -.validation-modal { - display: block; /* Show the modal for demo purposes */ - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgb(0,0,0); - background-color: rgba(0,0,0,0.4); - padding-top: 60px; -} -.modal-content { - background-color: #fefefe; - margin: 5% auto; - padding: 20px; - border: 1px solid #888; - width: 80%; - height: fit-content; -} -.modal-title { - font-size: 24px; - font-weight: bold; - margin-bottom: 20px; -} -.validation-box { - margin-bottom: 15px; - width: 100%; -} -.expansion-panel-header { - background-color: #e0e0e0; - padding: 10px; - cursor: pointer; -} -.expansion-panel-body { - display: none; - background-color: #fafafa; - padding: 10px; - border-top: 1px solid #ddd; -} -.expansion-panel-body pre { - background-color: #f6f8fa; - padding: 10px; - border-left: 4px solid #d1d5da; - overflow-x: auto; -} -.diff { - display: flex; - justify-content: space-between; - margin-bottom: 10px; -} -.diff-side { - width: 48%; - padding: 10px; -} -.diff-old { - background-color: #fee; - border: 1px solid #f00; -} -.diff-new { - background-color: #e6ffe6; - border: 1px solid #0f0; -} -.radio-buttons { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; -} \ No newline at end of file +.validation-modal { + display: block; /* Show the modal for demo purposes */ + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.4); + padding-top: 60px; +} +.modal-content { + background-color: #fefefe; + margin: 5% auto; + padding: 20px; + border: 1px solid #888; + width: 80%; + height: fit-content; +} +.modal-title { + font-size: 24px; + font-weight: bold; + margin-bottom: 20px; +} +.validation-box { + margin-bottom: 15px; + width: 100%; +} +.expansion-panel-header { + background-color: #e0e0e0; + padding: 10px; + cursor: pointer; +} +.expansion-panel-body { + display: none; + background-color: #fafafa; + padding: 10px; + border-top: 1px solid #ddd; +} +.expansion-panel-body pre { + background-color: #f6f8fa; + padding: 10px; + border-left: 4px solid #d1d5da; + overflow-x: auto; +} +.diff { + display: flex; + justify-content: space-between; + margin-bottom: 10px; +} +.diff-side { + width: 48%; + padding: 10px; +} +.diff-old { + background-color: #fee; + border: 1px solid #f00; +} +.diff-new { + background-color: #e6ffe6; + border: 1px solid #0f0; +} +.radio-buttons { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} diff --git a/src/components/validation-modal/validation-modal.html b/src/components/validation-modal/validation-modal.html index 409c189..780e489 100755 --- a/src/components/validation-modal/validation-modal.html +++ b/src/components/validation-modal/validation-modal.html @@ -1,59 +1,11 @@ -
- diff --git a/src/components/validation-modal/validation-modal.ts b/src/components/validation-modal/validation-modal.ts index 2076c11..711aff2 100755 --- a/src/components/validation-modal/validation-modal.ts +++ b/src/components/validation-modal/validation-modal.ts @@ -1,17 +1,56 @@ -import ModalService from "~/services/modal.service"; - -document.querySelectorAll('.expansion-panel-header').forEach(header => { - header.addEventListener('click', function(event) { - const target = event.target as HTMLElement - const body = target.nextElementSibling as HTMLElement; - if(body?.style) body.style.display = body.style.display === 'block' ? 'none' : 'block'; - }); -}); +import ModalService from '~/services/modal.service'; async function validate() { - console.log('==> VALIDATE') - const modalservice = await ModalService.getInstance() - modalservice.closeValidationModal() + console.log('==> VALIDATE'); + const modalservice = await ModalService.getInstance(); + modalservice.closeValidationModal(); } -(window as any).validate = validate \ No newline at end of file +export async function initValidationModal(processDiffs: any) { +console.log("🚀 ~ initValidationModal ~ processDiffs:", processDiffs) +for(const diff of processDiffs.diffs) { + let diffs = '' + for(const value of diff) { + diffs+= ` +
+ + +
+
+
+
-${value.previous_value}
+
+
+
+${value.new_value}
+
+
+ ` + } + + const state = ` +
+
State ${diff[0].new_state_merkle_root}
+
+ ${diffs} +
+
+ ` + const box = document.querySelector('.validation-box') + if(box) box.innerHTML += state +} +document.querySelectorAll('.expansion-panel-header').forEach((header) => { + header.addEventListener('click', function (event) { + const target = event.target as HTMLElement; + const body = target.nextElementSibling as HTMLElement; + if (body?.style) body.style.display = body.style.display === 'block' ? 'none' : 'block'; + }); +}); +} + +(window as any).validate = validate; diff --git a/src/decs.d.ts b/src/decs.d.ts index e482c3e..d137c88 100644 --- a/src/decs.d.ts +++ b/src/decs.d.ts @@ -1,10 +1,10 @@ -declare class AccountComponent extends HTMLElement { - _callback: any; - constructor(); - connectedCallback(): void; - fetchData(): Promise; - set callback(fn: any); - get callback(): any; - render(): void; -} -export { AccountComponent }; +declare class AccountComponent extends HTMLElement { + _callback: any; + constructor(); + connectedCallback(): void; + fetchData(): Promise; + set callback(fn: any); + get callback(): any; + render(): void; +} +export { AccountComponent }; diff --git a/src/interface/groupInterface.ts b/src/interface/groupInterface.ts index 13bba03..87ba2db 100644 --- a/src/interface/groupInterface.ts +++ b/src/interface/groupInterface.ts @@ -1,22 +1,22 @@ -import { DocumentSignature } from "~/models/signature.models"; - -export interface Group { - id: number; - name: string; - description: string; - roles: Array<{ - name: string; - members: Array<{ id: string | number; name: string }>; - documents?: Array; - }>; - commonDocuments: Array<{ - id: number; - name: string; - visibility: string; - description: string; - createdAt?: string | null; - deadline?: string | null; - signatures?: DocumentSignature[]; - status?: string; - }>; -} \ No newline at end of file +import { DocumentSignature } from '~/models/signature.models'; + +export interface Group { + id: number; + name: string; + description: string; + roles: Array<{ + name: string; + members: Array<{ id: string | number; name: string }>; + documents?: Array; + }>; + commonDocuments: Array<{ + id: number; + name: string; + visibility: string; + description: string; + createdAt?: string | null; + deadline?: string | null; + signatures?: DocumentSignature[]; + status?: string; + }>; +} diff --git a/src/interface/memberInterface.ts b/src/interface/memberInterface.ts index 20b80d2..274cd2c 100644 --- a/src/interface/memberInterface.ts +++ b/src/interface/memberInterface.ts @@ -1,7 +1,7 @@ -export interface Member { - id: string | number; - name: string; - email?: string; - avatar?: string; - processRoles?: Array<{ processId: number | string; role: string }>; -} +export interface Member { + id: string | number; + name: string; + email?: string; + avatar?: string; + processRoles?: Array<{ processId: number | string; role: string }>; +} diff --git a/src/main.ts b/src/main.ts index 14aaae1..054a322 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,38 +1,30 @@ -import { SignatureComponent } from './pages/signature/signature-component'; -import { SignatureElement } from './pages/signature/signature'; -import { ChatComponent } from './pages/chat/chat-component'; -import { ChatElement } from './pages/chat/chat'; -import { AccountComponent } from './pages/account/account-component'; -import { AccountElement } from './pages/account/account'; - -export { - SignatureComponent, - SignatureElement, - ChatComponent, - ChatElement, - AccountComponent, - AccountElement -}; - - -declare global { - interface HTMLElementTagNameMap { - 'signature-component': SignatureComponent; - 'signature-element': SignatureElement; - 'chat-component': ChatComponent; - 'chat-element': ChatElement; - 'account-component': AccountComponent; - 'account-element': AccountElement; - } -} - -// Configuration pour le mode indépendant -if ((import.meta as any).env.VITE_IS_INDEPENDANT_LIB) { - // Initialiser les composants si nécessaire - customElements.define('signature-component', SignatureComponent); - customElements.define('signature-element', SignatureElement); - customElements.define('chat-component', ChatComponent); - customElements.define('chat-element', ChatElement); - customElements.define('account-component', AccountComponent); - customElements.define('account-element', AccountElement); -} \ No newline at end of file +import { SignatureComponent } from './pages/signature/signature-component'; +import { SignatureElement } from './pages/signature/signature'; +import { ChatComponent } from './pages/chat/chat-component'; +import { ChatElement } from './pages/chat/chat'; +import { AccountComponent } from './pages/account/account-component'; +import { AccountElement } from './pages/account/account'; + +export { SignatureComponent, SignatureElement, ChatComponent, ChatElement, AccountComponent, AccountElement }; + +declare global { + interface HTMLElementTagNameMap { + 'signature-component': SignatureComponent; + 'signature-element': SignatureElement; + 'chat-component': ChatComponent; + 'chat-element': ChatElement; + 'account-component': AccountComponent; + 'account-element': AccountElement; + } +} + +// Configuration pour le mode indépendant +if ((import.meta as any).env.VITE_IS_INDEPENDANT_LIB) { + // Initialiser les composants si nécessaire + customElements.define('signature-component', SignatureComponent); + customElements.define('signature-element', SignatureElement); + customElements.define('chat-component', ChatComponent); + customElements.define('chat-element', ChatElement); + customElements.define('account-component', AccountComponent); + customElements.define('account-element', AccountElement); +} diff --git a/src/mocks/mock-account/constAccountMock.ts b/src/mocks/mock-account/constAccountMock.ts index 5beb139..34adaa4 100644 --- a/src/mocks/mock-account/constAccountMock.ts +++ b/src/mocks/mock-account/constAccountMock.ts @@ -1,328 +1,272 @@ -export const ALLOWED_ROLES = ['User', 'Member', 'Peer', 'Payment', 'Deposit', 'Artefact', 'Resolve', 'Backup']; - -export const STORAGE_KEYS = { - pairing: 'pairingRows', - wallet: 'walletRows', - process: 'processRows', - data: 'dataRows' -}; - -// Initialiser le stockage des lignes par défaut dans le localStorage -export const defaultRows = [ - { - column1: "sprt1qqwtvg5q5vcz0reqvmld98u7va3av6gakwe9yxw9yhnpj5djcunn4squ68tuzn8dz78dg4adfv0dekx8hg9sy0t6s9k5em7rffgxmrsfpyy7gtyrz", - column2: "🎊😑🎄😩", - column3: "Laptop" - }, - { - column1: "sprt1qqwtvg5q5vcz0reqvmld98u7va3av6gakwe9yxw9yhnpj5djcunn4squ68tuzn8dz78dg4adfv0dekx8hg9sy0t6s9k5em7rffgxmrsfpyy7gtyrx", - column2: "🎏🎕😧🌥", - column3: "Phone" } -]; - -export const mockNotifications: { [key: string]: Notification[] } = {}; - -export 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" -]; - -export 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" - } -]; - -export 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" } - ] - } - } -]; - - -export 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 project management." - }, - '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 procedures." - }, - 'Contract #789': { - title: "Member Access Agreement", - date: "2024-03-15", - parties: ["Company XYZ", "Member Team"], - terms: [ - "Member Rights", - "Access Levels", - "Security Protocol", - "Confidentiality Agreement" - ], - content: "This agreement outlines the terms for member access and privileges." - }, - 'Contract #101': { - title: "Peer Collaboration Agreement", - date: "2024-04-01", - parties: ["Company XYZ", "Peer Network"], - terms: [ - "Collaboration Rules", - "Resource Sharing", - "Dispute Resolution", - "Network Protocol" - ], - content: "This contract establishes peer collaboration and networking guidelines." - }, - 'Contract #102': { - title: "Payment Processing Agreement", - date: "2024-05-01", - parties: ["Company XYZ", "Payment Team"], - terms: [ - "Transaction Protocol", - "Security Measures", - "Fee Structure", - "Service Availability" - ], - content: "This agreement defines payment processing terms and conditions." - }, - 'Contract #103': { - title: "Deposit Management Contract", - date: "2024-06-01", - parties: ["Company XYZ", "Deposit Team"], - terms: [ - "Deposit Rules", - "Storage Protocol", - "Access Control", - "Security Standards" - ], - content: "This contract outlines deposit management procedures and security measures." - }, - 'Contract #104': { - title: "Artefact Handling Agreement", - date: "2024-07-01", - parties: ["Company XYZ", "Artefact Team"], - terms: [ - "Handling Procedures", - "Storage Guidelines", - "Access Protocol", - "Preservation Standards" - ], - content: "This agreement establishes artefact handling and preservation guidelines." - }, - 'Contract #105': { - title: "Resolution Protocol Agreement", - date: "2024-08-01", - parties: ["Company XYZ", "Resolution Team"], - terms: [ - "Resolution Process", - "Time Constraints", - "Escalation Protocol", - "Documentation Requirements" - ], - content: "This contract defines the resolution process and protocol standards." - }, - 'Contract #106': { - title: "Backup Service Agreement", - date: "2024-09-01", - parties: ["Company XYZ", "Backup Team"], - terms: [ - "Backup Schedule", - "Data Protection", - "Recovery Protocol", - "Service Reliability" - ], - content: "This agreement outlines backup service terms and recovery procedures." - } -}; +export const ALLOWED_ROLES = ['User', 'Member', 'Peer', 'Payment', 'Deposit', 'Artefact', 'Resolve', 'Backup']; + +export const STORAGE_KEYS = { + pairing: 'pairingRows', + wallet: 'walletRows', + process: 'processRows', + data: 'dataRows', +}; + +// Initialiser le stockage des lignes par défaut dans le localStorage +export const defaultRows = [ + { + column1: 'sprt1qqwtvg5q5vcz0reqvmld98u7va3av6gakwe9yxw9yhnpj5djcunn4squ68tuzn8dz78dg4adfv0dekx8hg9sy0t6s9k5em7rffgxmrsfpyy7gtyrz', + column2: '🎊😑🎄😩', + column3: 'Laptop', + }, + { + column1: 'sprt1qqwtvg5q5vcz0reqvmld98u7va3av6gakwe9yxw9yhnpj5djcunn4squ68tuzn8dz78dg4adfv0dekx8hg9sy0t6s9k5em7rffgxmrsfpyy7gtyrx', + column2: '🎏🎕😧🌥', + column3: 'Phone', + }, +]; + +export const mockNotifications: { [key: string]: Notification[] } = {}; + +export 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']; + +export 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', + }, +]; + +export 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' }, + ], + }, + }, +]; + +export 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 project management.', + }, + '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 procedures.', + }, + 'Contract #789': { + title: 'Member Access Agreement', + date: '2024-03-15', + parties: ['Company XYZ', 'Member Team'], + terms: ['Member Rights', 'Access Levels', 'Security Protocol', 'Confidentiality Agreement'], + content: 'This agreement outlines the terms for member access and privileges.', + }, + 'Contract #101': { + title: 'Peer Collaboration Agreement', + date: '2024-04-01', + parties: ['Company XYZ', 'Peer Network'], + terms: ['Collaboration Rules', 'Resource Sharing', 'Dispute Resolution', 'Network Protocol'], + content: 'This contract establishes peer collaboration and networking guidelines.', + }, + 'Contract #102': { + title: 'Payment Processing Agreement', + date: '2024-05-01', + parties: ['Company XYZ', 'Payment Team'], + terms: ['Transaction Protocol', 'Security Measures', 'Fee Structure', 'Service Availability'], + content: 'This agreement defines payment processing terms and conditions.', + }, + 'Contract #103': { + title: 'Deposit Management Contract', + date: '2024-06-01', + parties: ['Company XYZ', 'Deposit Team'], + terms: ['Deposit Rules', 'Storage Protocol', 'Access Control', 'Security Standards'], + content: 'This contract outlines deposit management procedures and security measures.', + }, + 'Contract #104': { + title: 'Artefact Handling Agreement', + date: '2024-07-01', + parties: ['Company XYZ', 'Artefact Team'], + terms: ['Handling Procedures', 'Storage Guidelines', 'Access Protocol', 'Preservation Standards'], + content: 'This agreement establishes artefact handling and preservation guidelines.', + }, + 'Contract #105': { + title: 'Resolution Protocol Agreement', + date: '2024-08-01', + parties: ['Company XYZ', 'Resolution Team'], + terms: ['Resolution Process', 'Time Constraints', 'Escalation Protocol', 'Documentation Requirements'], + content: 'This contract defines the resolution process and protocol standards.', + }, + 'Contract #106': { + title: 'Backup Service Agreement', + date: '2024-09-01', + parties: ['Company XYZ', 'Backup Team'], + terms: ['Backup Schedule', 'Data Protection', 'Recovery Protocol', 'Service Reliability'], + content: 'This agreement outlines backup service terms and recovery procedures.', + }, +}; diff --git a/src/mocks/mock-account/interfacesAccountMock.ts b/src/mocks/mock-account/interfacesAccountMock.ts index 0834d4c..1c409ba 100644 --- a/src/mocks/mock-account/interfacesAccountMock.ts +++ b/src/mocks/mock-account/interfacesAccountMock.ts @@ -1,45 +1,45 @@ -export interface Row { - column1: string; - column2: string; - column3: string; -} - -// Types supplémentaires nécessaires -export interface Contract { - title: string; - date: string; - parties: string[]; - terms: string[]; - content: string; -} - -export interface WalletRow { - column1: string; // Label - column2: string; // Wallet - column3: string; // Type -} - -export interface DataRow { - column1: string; // Name - column2: string; // Visibility - column3: string; // Role - column4: string; // Duration - column5: string; // Legal - column6: string; // Contract - processName: string; - zone: string; -} - -export interface Notification { - message: string; - timestamp: string; - isRead: boolean; -} - -// Déplacer l'interface en dehors de la classe, au début du fichier -export interface NotificationMessage { - id: number; - read: boolean; - date: string; - message: string; -} +export interface Row { + column1: string; + column2: string; + column3: string; +} + +// Types supplémentaires nécessaires +export interface Contract { + title: string; + date: string; + parties: string[]; + terms: string[]; + content: string; +} + +export interface WalletRow { + column1: string; // Label + column2: string; // Wallet + column3: string; // Type +} + +export interface DataRow { + column1: string; // Name + column2: string; // Visibility + column3: string; // Role + column4: string; // Duration + column5: string; // Legal + column6: string; // Contract + processName: string; + zone: string; +} + +export interface Notification { + message: string; + timestamp: string; + isRead: boolean; +} + +// Déplacer l'interface en dehors de la classe, au début du fichier +export interface NotificationMessage { + id: number; + read: boolean; + date: string; + message: string; +} diff --git a/src/mocks/mock-chat/groupsMock.js b/src/mocks/mock-chat/groupsMock.js index d567e26..d5d1fcf 100755 --- a/src/mocks/mock-chat/groupsMock.js +++ b/src/mocks/mock-chat/groupsMock.js @@ -1,40 +1,52 @@ -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" }] - } - ] - } -]; +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 index dfd85c7..28792d9 100755 --- a/src/mocks/mock-chat/messagesMock.js +++ b/src/mocks/mock-chat/messagesMock.js @@ -1,65 +1,64 @@ -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" } - ] - } - -]; +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 index e98588c..c652112 100755 --- a/src/mocks/mock-signature/groupsMock.js +++ b/src/mocks/mock-signature/groupsMock.js @@ -1,460 +1,471 @@ -// 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", - commonDocuments: [ - { - id: 101, - name: "Règlement intérieur", - description: "Document vierge pour le règlement intérieur", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 102, - name: "Charte de confidentialité", - description: "Document vierge pour la charte de confidentialité", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 103, - name: "Procédures générales", - description: "Document vierge pour les procédures générales", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 104, - name: "Urgency A", - description: "Document vierge pour le plan d'urgence A", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 105, - name: "Urgency B", - description: "Document vierge pour le plan d'urgence B", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 106, - name: "Urgency C", - description: "Document vierge pour le plan d'urgence C", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 107, - name: "Document à signer", - description: "Document vierge pour le règlement intérieur", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - ], - roles: [ - { - name: "User", - members: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }], - documents: [ - { - id: 1, - name: "Document User A", - description: "Description du document User A.", - visibility: "public", - createdAt: "2024-01-01", - deadline: "2024-02-01", - signatures: [ - { - member: { id: 1, name: "Alice" }, - signed: true, - signedAt: "2024-01-15" - }, - { - member: { id: 2, name: "Bob" }, - signed: false - } - ] - }, - { - id: 2, - name: "Document User B", - description: "Document vierge pour le rôle User", - visibility: "confidential", - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 7, - name: "Document User C", - description: "Document vierge pour validation utilisateur", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 8, - name: "Document User D", - description: "Document vierge pour approbation utilisateur", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - } - ] - }, - { - name: "Process", - members: [{ id: 3, name: "Charlie" }, { id: 4, name: "David" }], - documents: [ - { - id: 3, - name: "Document Process A", - description: "Description du document Process A.", - visibility: "confidential", - createdAt: "2024-01-10", - deadline: "2024-03-01", - signatures: [ - { - member: { id: 3, name: "Charlie" }, - signed: true, - signedAt: "2024-01-12" - } - ] - }, - { - id: 9, - name: "Document Process B", - description: "Document vierge pour processus interne", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 10, - name: "Document Process C", - description: "Document vierge pour validation processus", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 11, - name: "Document Process D", - description: "Document vierge pour validation processus", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.PENDING, - createdAt: "2024-01-15", - deadline: "2024-02-01", - signatures: [ - { - member: { id: 3, name: "Charlie" }, - signed: true, - signedAt: "2024-01-15" - }, - { - member: { id: 4, name: "David" }, - signed: false - } - ] - }, - { - id: 12, - name: "Document Process E", - description: "Document vierge pour validation processus", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.PENDING, - createdAt: "2024-01-15", - deadline: "2024-02-01", - signatures: [ - { - member: { id: 3, name: "Charlie" }, - signed: true, - signedAt: "2024-01-15" - }, - { - member: { id: 4, name: "David" }, - signed: false - } - ] - } - ] - }, - { - name: "Backup", - members: [{ id: 15, name: "Oscar" }, { id: 16, name: "Patricia" }], - documents: [ - { - id: 11, - name: "Document Backup A", - description: "Document vierge pour sauvegarde", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - } - ] - } - ] - }, - { - id: 2, - name: "Processus 2", - description: "Description du processus 2", - commonDocuments: [ - { - id: 201, - name: "Règlement intérieur", - description: "Document vierge pour le règlement intérieur", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 202, - name: "Charte de confidentialité", - description: "Document vierge pour la charte de confidentialité", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 203, - name: "Charte de confidentialité", - description: "Document vierge pour la charte de confidentialité", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 204, - name: "Charte de confidentialité", - description: "Document vierge pour la charte de confidentialité", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 205, - name: "Charte de confidentialité", - description: "Document vierge pour la charte de confidentialité", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - } - ], - roles: [ - { - name: "Artefact", - members: [{ id: 17, name: "Quinn" }, { id: 18, name: "Rachel" }], - documents: [ - { - id: 12, - name: "Document Artefact A", - description: "Document vierge pour artefact", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 13, - name: "Document Artefact B", - description: "Document vierge pour validation artefact", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - } - ] - }, - { - name: "Resolve", - members: [{ id: 19, name: "Sam" }, { id: 20, name: "Tom" }], - documents: [ - { - id: 14, - name: "Document Resolve A", - description: "Document vierge pour résolution", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - } - ] - } - ] - }, - { - id: 3, - name: "Processus 3", - description: "Description du processus 3", - commonDocuments: [ - { - id: 301, - name: "Règlement intérieur", - description: "Document vierge pour le règlement intérieur", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 302, - name: "Charte de confidentialité", - description: "Document vierge pour la charte de confidentialité", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 303, - name: "Procédures générales", - description: "Document vierge pour les procédures générales", - visibility: VISIBILITY_LEVELS.PUBLIC, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - } - ], - roles: [ - { - name: "Deposit", - members: [{ id: 21, name: "Uma" }, { id: 22, name: "Victor" }], - documents: [ - { - id: 15, - name: "Document Deposit A", - description: "Document vierge pour dépôt", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 16, - name: "Document Deposit B", - description: "Document vierge pour validation dépôt", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - } - ] - }, - { - name: "Payment", - members: [{ id: 23, name: "Walter" }, { id: 24, name: "Xena" }], - documents: [ - { - id: 17, - name: "Document Payment B", - description: "Document vierge pour paiement", - visibility: VISIBILITY_LEVELS.PRIVATE, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - }, - { - id: 18, - name: "Document Payment C", - description: "Document vierge pour validation paiement", - visibility: VISIBILITY_LEVELS.CONFIDENTIAL, - status: DOCUMENT_STATUS.DRAFT, - createdAt: null, - deadline: null, - signatures: [] - } - ] - } - ] - } -]; +// 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', + commonDocuments: [ + { + id: 101, + name: 'Règlement intérieur', + description: 'Document vierge pour le règlement intérieur', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 102, + name: 'Charte de confidentialité', + description: 'Document vierge pour la charte de confidentialité', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 103, + name: 'Procédures générales', + description: 'Document vierge pour les procédures générales', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 104, + name: 'Urgency A', + description: "Document vierge pour le plan d'urgence A", + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 105, + name: 'Urgency B', + description: "Document vierge pour le plan d'urgence B", + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 106, + name: 'Urgency C', + description: "Document vierge pour le plan d'urgence C", + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 107, + name: 'Document à signer', + description: 'Document vierge pour le règlement intérieur', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + roles: [ + { + name: 'User', + members: [ + { id: 1, name: 'Alice' }, + { id: 2, name: 'Bob' }, + ], + documents: [ + { + id: 1, + name: 'Document User A', + description: 'Description du document User A.', + visibility: 'public', + createdAt: '2024-01-01', + deadline: '2024-02-01', + signatures: [ + { + member: { id: 1, name: 'Alice' }, + signed: true, + signedAt: '2024-01-15', + }, + { + member: { id: 2, name: 'Bob' }, + signed: false, + }, + ], + }, + { + id: 2, + name: 'Document User B', + description: 'Document vierge pour le rôle User', + visibility: 'confidential', + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 7, + name: 'Document User C', + description: 'Document vierge pour validation utilisateur', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 8, + name: 'Document User D', + description: 'Document vierge pour approbation utilisateur', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + }, + { + name: 'Process', + members: [ + { id: 3, name: 'Charlie' }, + { id: 4, name: 'David' }, + ], + documents: [ + { + id: 3, + name: 'Document Process A', + description: 'Description du document Process A.', + visibility: 'confidential', + createdAt: '2024-01-10', + deadline: '2024-03-01', + signatures: [ + { + member: { id: 3, name: 'Charlie' }, + signed: true, + signedAt: '2024-01-12', + }, + ], + }, + { + id: 9, + name: 'Document Process B', + description: 'Document vierge pour processus interne', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 10, + name: 'Document Process C', + description: 'Document vierge pour validation processus', + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 11, + name: 'Document Process D', + description: 'Document vierge pour validation processus', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.PENDING, + createdAt: '2024-01-15', + deadline: '2024-02-01', + signatures: [ + { + member: { id: 3, name: 'Charlie' }, + signed: true, + signedAt: '2024-01-15', + }, + { + member: { id: 4, name: 'David' }, + signed: false, + }, + ], + }, + { + id: 12, + name: 'Document Process E', + description: 'Document vierge pour validation processus', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.PENDING, + createdAt: '2024-01-15', + deadline: '2024-02-01', + signatures: [ + { + member: { id: 3, name: 'Charlie' }, + signed: true, + signedAt: '2024-01-15', + }, + { + member: { id: 4, name: 'David' }, + signed: false, + }, + ], + }, + ], + }, + { + name: 'Backup', + members: [ + { id: 15, name: 'Oscar' }, + { id: 16, name: 'Patricia' }, + ], + documents: [ + { + id: 11, + name: 'Document Backup A', + description: 'Document vierge pour sauvegarde', + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + }, + ], + }, + { + id: 2, + name: 'Processus 2', + description: 'Description du processus 2', + commonDocuments: [ + { + id: 201, + name: 'Règlement intérieur', + description: 'Document vierge pour le règlement intérieur', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 202, + name: 'Charte de confidentialité', + description: 'Document vierge pour la charte de confidentialité', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 203, + name: 'Charte de confidentialité', + description: 'Document vierge pour la charte de confidentialité', + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 204, + name: 'Charte de confidentialité', + description: 'Document vierge pour la charte de confidentialité', + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 205, + name: 'Charte de confidentialité', + description: 'Document vierge pour la charte de confidentialité', + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + roles: [ + { + name: 'Artefact', + members: [ + { id: 17, name: 'Quinn' }, + { id: 18, name: 'Rachel' }, + ], + documents: [ + { + id: 12, + name: 'Document Artefact A', + description: 'Document vierge pour artefact', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 13, + name: 'Document Artefact B', + description: 'Document vierge pour validation artefact', + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + }, + { + name: 'Resolve', + members: [ + { id: 19, name: 'Sam' }, + { id: 20, name: 'Tom' }, + ], + documents: [ + { + id: 14, + name: 'Document Resolve A', + description: 'Document vierge pour résolution', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + }, + ], + }, + { + id: 3, + name: 'Processus 3', + description: 'Description du processus 3', + commonDocuments: [ + { + id: 301, + name: 'Règlement intérieur', + description: 'Document vierge pour le règlement intérieur', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 302, + name: 'Charte de confidentialité', + description: 'Document vierge pour la charte de confidentialité', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 303, + name: 'Procédures générales', + description: 'Document vierge pour les procédures générales', + visibility: VISIBILITY_LEVELS.PUBLIC, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + roles: [ + { + name: 'Deposit', + members: [ + { id: 21, name: 'Uma' }, + { id: 22, name: 'Victor' }, + ], + documents: [ + { + id: 15, + name: 'Document Deposit A', + description: 'Document vierge pour dépôt', + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 16, + name: 'Document Deposit B', + description: 'Document vierge pour validation dépôt', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + }, + { + name: 'Payment', + members: [ + { id: 23, name: 'Walter' }, + { id: 24, name: 'Xena' }, + ], + documents: [ + { + id: 17, + name: 'Document Payment B', + description: 'Document vierge pour paiement', + visibility: VISIBILITY_LEVELS.PRIVATE, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + { + id: 18, + name: 'Document Payment C', + description: 'Document vierge pour validation paiement', + visibility: VISIBILITY_LEVELS.CONFIDENTIAL, + status: DOCUMENT_STATUS.DRAFT, + createdAt: null, + deadline: null, + signatures: [], + }, + ], + }, + ], + }, +]; diff --git a/src/mocks/mock-signature/membersMocks.js b/src/mocks/mock-signature/membersMocks.js index 3d5bf77..2ffc6ce 100755 --- a/src/mocks/mock-signature/membersMocks.js +++ b/src/mocks/mock-signature/membersMocks.js @@ -1,105 +1,105 @@ -export const membersMock = [ - // Processus 1 - { - id: 1, - name: "Alice", - avatar: "A", - email: "alice@company.com", - processRoles: [{ processId: 1, role: "User" }] - }, - { - id: 2, - name: "Bob", - avatar: "B", - email: "bob@company.com", - processRoles: [{ processId: 1, role: "User" }] - }, - { - id: 3, - name: "Charlie", - avatar: "C", - email: "charlie@company.com", - processRoles: [{ processId: 1, role: "Process" }] - }, - { - id: 4, - name: "David", - avatar: "D", - email: "david@company.com", - processRoles: [{ processId: 1, role: "Process" }] - }, - { - id: 15, - name: "Oscar", - avatar: "O", - email: "oscar@company.com", - processRoles: [{ processId: 1, role: "Backup" }] - }, - { - id: 16, - name: "Patricia", - avatar: "P", - email: "patricia@company.com", - processRoles: [{ processId: 1, role: "Backup" }] - }, - - // Processus 2 - { - id: 17, - name: "Quinn", - avatar: "Q", - email: "quinn@company.com", - processRoles: [{ processId: 2, role: "Artefact" }] - }, - { - id: 18, - name: "Rachel", - avatar: "R", - email: "rachel@company.com", - processRoles: [{ processId: 2, role: "Artefact" }] - }, - { - id: 19, - name: "Sam", - avatar: "S", - email: "sam@company.com", - processRoles: [{ processId: 2, role: "Resolve" }] - }, - { - id: 20, - name: "Tom", - avatar: "T", - email: "tom@company.com", - processRoles: [{ processId: 2, role: "Resolve" }] - }, - - // Processus 3 - { - id: 21, - name: "Uma", - avatar: "U", - email: "uma@company.com", - processRoles: [{ processId: 3, role: "Deposit" }] - }, - { - id: 22, - name: "Victor", - avatar: "V", - email: "victor@company.com", - processRoles: [{ processId: 3, role: "Deposit" }] - }, - { - id: 23, - name: "Walter", - avatar: "W", - email: "walter@company.com", - processRoles: [{ processId: 3, role: "Payment" }] - }, - { - id: 24, - name: "Xena", - avatar: "X", - email: "xena@company.com", - processRoles: [{ processId: 3, role: "Payment" }] - } -]; +export const membersMock = [ + // Processus 1 + { + id: 1, + name: 'Alice', + avatar: 'A', + email: 'alice@company.com', + processRoles: [{ processId: 1, role: 'User' }], + }, + { + id: 2, + name: 'Bob', + avatar: 'B', + email: 'bob@company.com', + processRoles: [{ processId: 1, role: 'User' }], + }, + { + id: 3, + name: 'Charlie', + avatar: 'C', + email: 'charlie@company.com', + processRoles: [{ processId: 1, role: 'Process' }], + }, + { + id: 4, + name: 'David', + avatar: 'D', + email: 'david@company.com', + processRoles: [{ processId: 1, role: 'Process' }], + }, + { + id: 15, + name: 'Oscar', + avatar: 'O', + email: 'oscar@company.com', + processRoles: [{ processId: 1, role: 'Backup' }], + }, + { + id: 16, + name: 'Patricia', + avatar: 'P', + email: 'patricia@company.com', + processRoles: [{ processId: 1, role: 'Backup' }], + }, + + // Processus 2 + { + id: 17, + name: 'Quinn', + avatar: 'Q', + email: 'quinn@company.com', + processRoles: [{ processId: 2, role: 'Artefact' }], + }, + { + id: 18, + name: 'Rachel', + avatar: 'R', + email: 'rachel@company.com', + processRoles: [{ processId: 2, role: 'Artefact' }], + }, + { + id: 19, + name: 'Sam', + avatar: 'S', + email: 'sam@company.com', + processRoles: [{ processId: 2, role: 'Resolve' }], + }, + { + id: 20, + name: 'Tom', + avatar: 'T', + email: 'tom@company.com', + processRoles: [{ processId: 2, role: 'Resolve' }], + }, + + // Processus 3 + { + id: 21, + name: 'Uma', + avatar: 'U', + email: 'uma@company.com', + processRoles: [{ processId: 3, role: 'Deposit' }], + }, + { + id: 22, + name: 'Victor', + avatar: 'V', + email: 'victor@company.com', + processRoles: [{ processId: 3, role: 'Deposit' }], + }, + { + id: 23, + name: 'Walter', + avatar: 'W', + email: 'walter@company.com', + processRoles: [{ processId: 3, role: 'Payment' }], + }, + { + id: 24, + name: 'Xena', + avatar: 'X', + email: 'xena@company.com', + processRoles: [{ processId: 3, role: 'Payment' }], + }, +]; diff --git a/src/mocks/mock-signature/messagesMock.ts b/src/mocks/mock-signature/messagesMock.ts index c00cbfd..ca1c31e 100755 --- a/src/mocks/mock-signature/messagesMock.ts +++ b/src/mocks/mock-signature/messagesMock.ts @@ -1,65 +1,64 @@ -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" } - ] - } - -]; +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/notification.model.ts b/src/models/notification.model.ts index 3ef66d6..0d78630 100755 --- a/src/models/notification.model.ts +++ b/src/models/notification.model.ts @@ -19,12 +19,12 @@ export interface IMessage { } export interface UserDiff { - new_state_merkle_root: string, // TODO add a merkle proof that the new_value belongs to that state - field: string, - previous_value: string, - new_value: string, - notify_user: boolean, - need_validation: boolean, + new_state_merkle_root: string; // TODO add a merkle proof that the new_value belongs to that state + field: string; + previous_value: string; + new_value: string; + notify_user: boolean; + need_validation: boolean; // validated: bool, - proof: any, // This is only validation (or refusal) for that specific diff, not the whole state. It can't be commited as such -} \ No newline at end of file + proof: any; // This is only validation (or refusal) for that specific diff, not the whole state. It can't be commited as such +} diff --git a/src/models/signature.models.ts b/src/models/signature.models.ts index d5b7888..ef5ad28 100755 --- a/src/models/signature.models.ts +++ b/src/models/signature.models.ts @@ -1,59 +1,59 @@ -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; - files?: Array<{ name: string; url: 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 +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; + files?: Array<{ name: string; url: 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; +} diff --git a/src/pages/account/account-component.ts b/src/pages/account/account-component.ts index b550af1..db8f193 100644 --- a/src/pages/account/account-component.ts +++ b/src/pages/account/account-component.ts @@ -1,63 +1,62 @@ -import { AccountElement } from './account'; -import accountCss from '../../../public/style/account.css?raw' -import Services from '../../services/service.js' - -class AccountComponent extends HTMLElement { - _callback: any - accountElement: AccountElement | null = null; - - constructor() { - super(); - console.log('INIT') - this.attachShadow({ mode: 'open' }); - - this.accountElement = this.shadowRoot?.querySelector('account-element') || null; - } - - connectedCallback() { - console.log('CALLBACKs') - this.render(); - this.fetchData(); - - if (!customElements.get('account-element')) { - customElements.define('account-element', AccountElement); - } - } - - async fetchData() { - if ((import.meta as any).env.VITE_IS_INDEPENDANT_LIB === false) { - const data = await (window as any).myService?.getProcesses(); - } else { - const service = await Services.getInstance() - const data = await service.getProcesses(); - } - } - - set callback(fn) { - if (typeof fn === 'function') { - this._callback = fn; - } else { - console.error('Callback is not a function'); - } - } - - get callback() { - return this._callback; - } - - render() { - if(this.shadowRoot && !this.shadowRoot.querySelector('account-element')) { - const style = document.createElement('style'); - style.textContent = accountCss; - - const accountElement = document.createElement('account-element'); - - this.shadowRoot.appendChild(style); - this.shadowRoot.appendChild(accountElement); - - } - } -} - -export { AccountComponent } -customElements.define('account-component', AccountComponent); +import { AccountElement } from './account'; +import accountCss from '../../../public/style/account.css?raw'; +import Services from '../../services/service.js'; + +class AccountComponent extends HTMLElement { + _callback: any; + accountElement: AccountElement | null = null; + + constructor() { + super(); + console.log('INIT'); + this.attachShadow({ mode: 'open' }); + + this.accountElement = this.shadowRoot?.querySelector('account-element') || null; + } + + connectedCallback() { + console.log('CALLBACKs'); + this.render(); + this.fetchData(); + + if (!customElements.get('account-element')) { + customElements.define('account-element', AccountElement); + } + } + + async fetchData() { + if ((import.meta as any).env.VITE_IS_INDEPENDANT_LIB === false) { + const data = await (window as any).myService?.getProcesses(); + } else { + const service = await Services.getInstance(); + const data = await service.getProcesses(); + } + } + + set callback(fn) { + if (typeof fn === 'function') { + this._callback = fn; + } else { + console.error('Callback is not a function'); + } + } + + get callback() { + return this._callback; + } + + render() { + if (this.shadowRoot && !this.shadowRoot.querySelector('account-element')) { + const style = document.createElement('style'); + style.textContent = accountCss; + + const accountElement = document.createElement('account-element'); + + this.shadowRoot.appendChild(style); + this.shadowRoot.appendChild(accountElement); + } + } +} + +export { AccountComponent }; +customElements.define('account-component', AccountComponent); diff --git a/src/pages/account/account.html b/src/pages/account/account.html index 3b88b7f..e62e0e4 100755 --- a/src/pages/account/account.html +++ b/src/pages/account/account.html @@ -1,91 +1,99 @@ - - - - - - Account - - - - - -
- - - - - -
- -
-
    Pairing 🔗
-
    Wallet 👛
-
    Process ⚙️
-
    Data 💾
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file + + + + + + Account + + + + + +
+ + + + + +
+ +
+
    + Pairing 🔗 +
+
    + Wallet 👛 +
+
    + Process ⚙️ +
+
    + Data 💾 +
+
+ + +
+
+
+
+
+
+
+
+
+ + + + + diff --git a/src/pages/account/account.ts b/src/pages/account/account.ts index 75ca422..4e384b3 100755 --- a/src/pages/account/account.ts +++ b/src/pages/account/account.ts @@ -1,1266 +1,1262 @@ -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; - showAlert: (title: string, text?: string, icon?: string) => void; - addRow: () => void; - confirmRow: () => void; - cancelRow: () => void; - deleteRow: (button: HTMLButtonElement) => void; - generateRecoveryWords: () => string[]; - exportUserData: () => void; - updateActionButtons: () => void; - } -} - -import Swal from 'sweetalert2'; -import { STORAGE_KEYS, defaultRows, mockProcessRows, mockNotifications, notificationMessages, mockDataRows, mockContracts, ALLOWED_ROLES } from '../../mocks/mock-account/constAccountMock'; -import { Row, WalletRow, DataRow, Notification, Contract, NotificationMessage } from '../../mocks/mock-account/interfacesAccountMock'; -import { addressToEmoji } from '../../utils/sp-address.utils'; -import { getCorrectDOM } from '../../utils/document.utils'; - -let isAddingRow = false; -let currentRow: HTMLTableRowElement | null = null; -let currentMode: keyof typeof STORAGE_KEYS = 'pairing'; - -export 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); -} - -class AccountElement extends HTMLElement { - private dom: Node; - - - - - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - this.dom = getCorrectDOM('account-element'); - - - window.showPairing = () => this.showPairing(); - window.showWallet = () => this.showWallet(); - window.showProcess = () => this.showProcess(); - window.showData = () => this.showData(); - window.addWalletRow = () => this.addWalletRow(); - window.confirmWalletRow = () => this.confirmWalletRow(); - window.cancelWalletRow = () => this.cancelWalletRow(); - window.editDeviceName = (cell: HTMLTableCellElement) => this.editDeviceName(cell); - window.showProcessNotifications = (processName: string) => this.showProcessNotifications(processName); - window.handleLogout = () => this.handleLogout(); - window.confirmDeleteAccount = () => this.confirmDeleteAccount(); - window.showContractPopup = (contractId: string) => this.showContractPopup(contractId); - window.addRow = () => this.addRow(); - window.deleteRow = (button: HTMLButtonElement) => this.deleteRow(button); - window.confirmRow = () => this.confirmRow(); - window.cancelRow = () => this.cancelRow(); - window.updateNavbarBanner = (bannerUrl: string) => this.updateNavbarBanner(bannerUrl); - window.saveBannerToLocalStorage = (bannerUrl: string) => this.saveBannerToLocalStorage(bannerUrl); - window.loadSavedBanner = () => this.loadSavedBanner(); - window.closeNotificationPopup = (event: Event) => this.closeNotificationPopup(event); - window.markAsRead = (processName: string, messageId: number, element: HTMLElement) => this.markAsRead(processName, messageId, element); - window.exportRecovery = () => this.exportRecovery(); - window.generateRecoveryWords = () => this.generateRecoveryWords(); - window.exportUserData = () => this.exportUserData(); - window.updateActionButtons = () => this.updateActionButtons(); - window.openAvatarPopup = () => this.openAvatarPopup(); - window.closeAvatarPopup = () => this.closeAvatarPopup(); - - if (!localStorage.getItem('rows')) { - localStorage.setItem('rows', JSON.stringify(defaultRows)); - } - } - - // Fonctions de gestion des comptes et de l'interface utilisateur - private 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', () => { - this.deleteAccount(); - modal.remove(); - }); -} - -private deleteAccount(): void { - localStorage.clear(); - window.location.href = '/login.html'; -} - -private 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; -} - -private saveBannerToLocalStorage(dataUrl: string): void { - localStorage.setItem('userBanner', dataUrl); -} - -private loadSavedBanner(): void { - const savedBanner = localStorage.getItem('userBanner'); - if (savedBanner) { - const bannerImg = document.getElementById('popup-banner-img') as HTMLImageElement; - if (bannerImg) { - bannerImg.src = savedBanner; - } - this.updateNavbarBanner(savedBanner); - } -} - - -private 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(); -} - -private 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 = this.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 -private 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 -private 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 = this.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'; - } - } - }); - } - }); -} - -private generateRecoveryWords(): string[] { - const wordsList = [ - 'apple', 'banana', 'orange', 'grape', 'kiwi', 'mango', 'peach', 'plum', - 'lemon', 'lime', 'cherry', 'melon', 'pear', 'fig', 'date', 'berry' - ]; - 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; -} - -private 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); -} - -private updateActionButtons(): void { - const buttonContainer = document.querySelector('.button-container'); - if (!buttonContainer) return; - - buttonContainer.innerHTML = ` -
- - -
- `; -} - -private getConfirmFunction(): string { - switch (currentMode) { - case 'wallet': - return 'window.confirmWalletRow()'; - case 'process': - return 'window.confirmProcessRow()'; - default: - return 'window.confirmRow()'; - } -} - -private getCancelFunction(): string { - switch (currentMode) { - case 'wallet': - return 'window.cancelWalletRow()'; - case 'process': - return 'window.cancelProcessRow()'; - default: - return 'window.cancelRow()'; - } -} - -// Fonctions de gestion des tableaux -private 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) { - input.addEventListener('input', async (e) => { - const addressInput = e.target as HTMLInputElement; - const emojiCell = currentRow!.cells[2]; - const emojis = await addressToEmoji(addressInput.value); - if (emojiCell.querySelector('input')) { - (emojiCell.querySelector('input') as HTMLInputElement).value = emojis; - } - }); - } - - if (index === 2) { - input.readOnly = true; - } - - cell.appendChild(input); - }); - - const deleteCell = currentRow.insertCell(); - deleteCell.style.width = '40px'; - - this.updateActionButtons(); -} - -// Fonctions de mise à jour de l'interface -private 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(''); -} - - - -private 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; - - this.resetButtonContainer(); - this.updateTableContent(rows); -} - -private cancelRow(): void { - if (!currentRow) return; - - currentRow.remove(); - isAddingRow = false; - currentRow = null; - - this.resetButtonContainer(); -} - -private resetButtonContainer(): void { - const buttonContainer = document.querySelector('.button-container'); - if (!buttonContainer) return; - - buttonContainer.innerHTML = ` - - `; -} - -private 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); -} - -private 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', () => this.finishEditing(cell, input)); - input.addEventListener('keypress', (e: KeyboardEvent) => { - if (e.key === 'Enter') { - this.finishEditing(cell, input); - } - }); - - cell.textContent = ''; - cell.appendChild(input); - cell.classList.add('editing'); - input.focus(); -} - -private 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].column2 = newValue; - localStorage.setItem(storageKey, JSON.stringify(rows)); - } - - cell.textContent = newValue; - cell.classList.remove('editing'); -} -// Fonction pour gérer le téléchargement de l'avatar -private 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); - } -} - - -private showProcess(): void { - //console.log("showProcess called"); - currentMode = 'process'; - this.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 - this.updateProcessTableContent(mockProcessRows); - } -} - -// Fonction utilitaire pour mettre à jour le contenu du tableau Process -private 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(''); -} - - - -private 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 = this.calculateNotifications(process.notification.messages); - countElement.textContent = `${notifCount.unread}/${notifCount.total}`; - } - - const closeButton = modal.querySelector('.close-notifications'); - closeButton?.addEventListener('click', () => { - modal.remove(); - this.showProcess(); // Rafraîchir l'affichage pour mettre à jour les compteurs - }); -} - - -private handleLogout(): void { - localStorage.clear(); - window.location.href = '../login/login.html'; -} - - -// Fonctions de gestion des contrats -private showContractPopup(contractId: string) { - // Empêcher la navigation par défaut - event?.preventDefault(); - // Check if the contract exists in mockContracts - const contract = mockContracts[contractId as keyof typeof mockContracts]; - if (!contract) { - console.error('Contract not found:', contractId); - return; - } - - // Créer la popup - const popup = document.createElement('div'); - popup.className = 'contract-popup-overlay'; - popup.innerHTML = ` -
- -

${contract.title}

-
-

Date: ${contract.date}

-

Parties: ${contract.parties.join(', ')}

-

Terms:

-
    - ${contract.terms.map(term => `
  • ${term}
  • `).join('')} -
-

Content: ${contract.content}

-
-
- `; - - // Ajouter la popup au body - document.body.appendChild(popup); - - // Gérer la fermeture - const closeBtn = popup.querySelector('.close-contract-popup'); - const closePopup = () => popup.remove(); - - closeBtn?.addEventListener('click', closePopup); - popup.addEventListener('click', (e) => { - if (e.target === popup) closePopup(); - }); -} - -// Ajouter à l'objet window - - -// Fonction utilitaire pour cacher tous les contenus -private 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 -private showPairing(): void { - - isAddingRow = false; - currentRow = null; - - currentMode = 'pairing'; - // Cacher tous les contenus - this.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) || '[]'); - this.updateTableContent(rows); - } -} - -private showWallet(): void { - isAddingRow = false; - currentRow = null; - - currentMode = 'wallet'; - this.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'; - walletContent.innerHTML = ` -
Wallet
-
- - - - - - - - - -
LabelWalletType
-
- -
-
- `; - - const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || '[]'); - this.updateWalletTableContent(rows); -} - - -private 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(''); -} - -private showData(): void { - //console.log("showData called"); - currentMode = 'data'; - this.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) || '[]'); - this.updateDataTableContent(rows); - } -} - -// Fonctions de gestion du wallet -private 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 = ` -
- - -
- `; - - this.updateActionButtons(); -} - -private 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; - this.showWallet(); - } else { - showAlert('Please complete all fields before confirming.'); - } -} - -private 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 = ` - - `; - - -} - -private 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 -private openAvatarPopup(): void { - const popup = document.getElementById('avatar-popup'); - if (!popup) return; - - // Récupérer les valeurs stockées - const savedName = localStorage.getItem('userName'); - const savedLastName = localStorage.getItem('userLastName'); - 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'; - this.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'; - } - } -} - -private 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 = popup.querySelector('#popup-avatar-img') as HTMLImageElement; - const navAvatar = document.querySelector('.nav-wrapper .avatar') as HTMLImageElement; - const headerAvatar = document.querySelector('header-element')?.shadowRoot?.querySelector('.avatar') as HTMLImageElement; - - if (popupAvatar) popupAvatar.src = result; - if (navAvatar) navAvatar.src = result; - if (headerAvatar) headerAvatar.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 = popup.querySelector('#popup-banner-img') as HTMLImageElement; - const navBanner = document.querySelector('.nav-wrapper .banner-image') as HTMLImageElement; - const headerBanner = document.querySelector('header-element')?.shadowRoot?.querySelector('.banner-image') as HTMLImageElement; - - if (popupBanner) popupBanner.src = result; - if (navBanner) navBanner.src = result; - if (headerBanner) headerBanner.src = 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', () => { - const newName = nameInput.value; - localStorage.setItem('userName', newName); - - const headerName = document.querySelector('header-element')?.shadowRoot?.querySelector('.user-name'); - const navName = document.querySelector('.nav-wrapper .user-name'); - - if (headerName) headerName.textContent = newName; - if (navName) navName.textContent = newName; - }); - } - - if (lastNameInput) { - lastNameInput.addEventListener('input', () => { - const newLastName = lastNameInput.value; - console.log('Nom de famille mis à jour :', newLastName); - localStorage.setItem('userLastName', newLastName); - - const headerLastName = document.querySelector('header-element')?.shadowRoot?.querySelector('.user-lastname'); - const navLastName = document.querySelector('.nav-wrapper .user-lastname'); - - if (headerLastName) headerLastName.textContent = newLastName; - if (navLastName) navLastName.textContent = newLastName; - }); - } -} - -private closeAvatarPopup(): void { - const popup = document.querySelector('.avatar-popup'); - if (popup) popup.remove(); -} - -private loadAvatar(): void { - const savedAvatar = localStorage.getItem('userAvatar'); - if (savedAvatar) { - const avatarImg = document.querySelector('.avatar') as HTMLImageElement; - if (avatarImg) { - avatarImg.src = savedAvatar; - } - } -} - -private loadUserInfo(): void { - const savedName = localStorage.getItem('userName'); - const savedLastName = localStorage.getItem('userLastName'); - - console.log('Nom récupéré :', savedName); - console.log('Nom de famille récupéré :', savedLastName); - - 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; - } - } - - this.loadAvatar(); - this.loadSavedBanner(); -} - - - private updateNavbarName(name: string): void { - const nameElement = document.querySelector('.nav-wrapper .user-name'); - if (nameElement) { - nameElement.textContent = name; - } - } - - private updateNavbarLastName(lastName: string): void { - const lastNameElement = document.querySelector('.nav-wrapper .user-lastname'); - if (lastNameElement) { - lastNameElement.textContent = lastName; - } - } - - - - private initializeEventListeners() { - document.addEventListener('DOMContentLoaded', () => { - this.showPairing(); - }); - - 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', this.handleAvatarUpload); - } - } - - connectedCallback() { - this.initializeEventListeners(); - this.loadSavedBanner(); - this.loadUserInfo(); - - - const savedAvatar = localStorage.getItem('userAvatar'); - const savedBanner = localStorage.getItem('userBanner'); - const savedName = localStorage.getItem('userName'); - const savedLastName = localStorage.getItem('userLastName'); - - if (savedAvatar) { - const navAvatar = this.shadowRoot?.querySelector('.avatar') as HTMLImageElement; - if (navAvatar) navAvatar.src = savedAvatar; - } - - if (savedBanner) { - const navBanner = this.shadowRoot?.querySelector('.banner-image') as HTMLImageElement; - if (navBanner) navBanner.src = savedBanner; - } - - if (savedName) { - this.updateNavbarName(savedName); - } - if (savedLastName) { - this.updateNavbarLastName(savedLastName); - } - } -} - -customElements.define('account-element', AccountElement); -export { AccountElement }; - - - +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; + showAlert: (title: string, text?: string, icon?: string) => void; + addRow: () => void; + confirmRow: () => void; + cancelRow: () => void; + deleteRow: (button: HTMLButtonElement) => void; + generateRecoveryWords: () => string[]; + exportUserData: () => void; + updateActionButtons: () => void; + } +} + +import Swal from 'sweetalert2'; +import { STORAGE_KEYS, defaultRows, mockProcessRows, mockNotifications, notificationMessages, mockDataRows, mockContracts, ALLOWED_ROLES } from '../../mocks/mock-account/constAccountMock'; +import { Row, WalletRow, DataRow, Notification, Contract, NotificationMessage } from '../../mocks/mock-account/interfacesAccountMock'; +import { addressToEmoji } from '../../utils/sp-address.utils'; +import { getCorrectDOM } from '../../utils/document.utils'; + +let isAddingRow = false; +let currentRow: HTMLTableRowElement | null = null; +let currentMode: keyof typeof STORAGE_KEYS = 'pairing'; + +export 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); +} + +class AccountElement extends HTMLElement { + private dom: Node; + + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this.dom = getCorrectDOM('account-element'); + + window.showPairing = () => this.showPairing(); + window.showWallet = () => this.showWallet(); + window.showProcess = () => this.showProcess(); + window.showData = () => this.showData(); + window.addWalletRow = () => this.addWalletRow(); + window.confirmWalletRow = () => this.confirmWalletRow(); + window.cancelWalletRow = () => this.cancelWalletRow(); + window.editDeviceName = (cell: HTMLTableCellElement) => this.editDeviceName(cell); + window.showProcessNotifications = (processName: string) => this.showProcessNotifications(processName); + window.handleLogout = () => this.handleLogout(); + window.confirmDeleteAccount = () => this.confirmDeleteAccount(); + window.showContractPopup = (contractId: string) => this.showContractPopup(contractId); + window.addRow = () => this.addRow(); + window.deleteRow = (button: HTMLButtonElement) => this.deleteRow(button); + window.confirmRow = () => this.confirmRow(); + window.cancelRow = () => this.cancelRow(); + window.updateNavbarBanner = (bannerUrl: string) => this.updateNavbarBanner(bannerUrl); + window.saveBannerToLocalStorage = (bannerUrl: string) => this.saveBannerToLocalStorage(bannerUrl); + window.loadSavedBanner = () => this.loadSavedBanner(); + window.closeNotificationPopup = (event: Event) => this.closeNotificationPopup(event); + window.markAsRead = (processName: string, messageId: number, element: HTMLElement) => this.markAsRead(processName, messageId, element); + window.exportRecovery = () => this.exportRecovery(); + window.generateRecoveryWords = () => this.generateRecoveryWords(); + window.exportUserData = () => this.exportUserData(); + window.updateActionButtons = () => this.updateActionButtons(); + window.openAvatarPopup = () => this.openAvatarPopup(); + window.closeAvatarPopup = () => this.closeAvatarPopup(); + + if (!localStorage.getItem('rows')) { + localStorage.setItem('rows', JSON.stringify(defaultRows)); + } + } + + // Fonctions de gestion des comptes et de l'interface utilisateur + private 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', () => { + this.deleteAccount(); + modal.remove(); + }); + } + + private deleteAccount(): void { + localStorage.clear(); + window.location.href = '/login.html'; + } + + private 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; + } + + private saveBannerToLocalStorage(dataUrl: string): void { + localStorage.setItem('userBanner', dataUrl); + } + + private loadSavedBanner(): void { + const savedBanner = localStorage.getItem('userBanner'); + if (savedBanner) { + const bannerImg = document.getElementById('popup-banner-img') as HTMLImageElement; + if (bannerImg) { + bannerImg.src = savedBanner; + } + this.updateNavbarBanner(savedBanner); + } + } + + private 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(); + } + + private 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 = this.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 + private 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 + private 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 = this.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'; + } + } + }); + } + }); + } + + private generateRecoveryWords(): string[] { + const wordsList = ['apple', 'banana', 'orange', 'grape', 'kiwi', 'mango', 'peach', 'plum', 'lemon', 'lime', 'cherry', 'melon', 'pear', 'fig', 'date', 'berry']; + 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; + } + + private 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); + } + + private updateActionButtons(): void { + const buttonContainer = document.querySelector('.button-container'); + if (!buttonContainer) return; + + buttonContainer.innerHTML = ` +
+ + +
+ `; + } + + private getConfirmFunction(): string { + switch (currentMode) { + case 'wallet': + return 'window.confirmWalletRow()'; + case 'process': + return 'window.confirmProcessRow()'; + default: + return 'window.confirmRow()'; + } + } + + private getCancelFunction(): string { + switch (currentMode) { + case 'wallet': + return 'window.cancelWalletRow()'; + case 'process': + return 'window.cancelProcessRow()'; + default: + return 'window.cancelRow()'; + } + } + + // Fonctions de gestion des tableaux + private 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) { + input.addEventListener('input', async (e) => { + const addressInput = e.target as HTMLInputElement; + const emojiCell = currentRow!.cells[2]; + const emojis = await addressToEmoji(addressInput.value); + if (emojiCell.querySelector('input')) { + (emojiCell.querySelector('input') as HTMLInputElement).value = emojis; + } + }); + } + + if (index === 2) { + input.readOnly = true; + } + + cell.appendChild(input); + }); + + const deleteCell = currentRow.insertCell(); + deleteCell.style.width = '40px'; + + this.updateActionButtons(); + } + + // Fonctions de mise à jour de l'interface + private 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(''); + } + + private 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; + + this.resetButtonContainer(); + this.updateTableContent(rows); + } + + private cancelRow(): void { + if (!currentRow) return; + + currentRow.remove(); + isAddingRow = false; + currentRow = null; + + this.resetButtonContainer(); + } + + private resetButtonContainer(): void { + const buttonContainer = document.querySelector('.button-container'); + if (!buttonContainer) return; + + buttonContainer.innerHTML = ` + + `; + } + + private 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); + } + + private 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', () => this.finishEditing(cell, input)); + input.addEventListener('keypress', (e: KeyboardEvent) => { + if (e.key === 'Enter') { + this.finishEditing(cell, input); + } + }); + + cell.textContent = ''; + cell.appendChild(input); + cell.classList.add('editing'); + input.focus(); + } + + private 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].column2 = newValue; + localStorage.setItem(storageKey, JSON.stringify(rows)); + } + + cell.textContent = newValue; + cell.classList.remove('editing'); + } + // Fonction pour gérer le téléchargement de l'avatar + private 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); + } + } + + private showProcess(): void { + //console.log("showProcess called"); + currentMode = 'process'; + this.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 + this.updateProcessTableContent(mockProcessRows); + } + } + + // Fonction utilitaire pour mettre à jour le contenu du tableau Process + private 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(''); + } + + private 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 = this.calculateNotifications(process.notification.messages); + countElement.textContent = `${notifCount.unread}/${notifCount.total}`; + } + + const closeButton = modal.querySelector('.close-notifications'); + closeButton?.addEventListener('click', () => { + modal.remove(); + this.showProcess(); // Rafraîchir l'affichage pour mettre à jour les compteurs + }); + } + + private handleLogout(): void { + localStorage.clear(); + window.location.href = '../login/login.html'; + } + + // Fonctions de gestion des contrats + private showContractPopup(contractId: string) { + // Empêcher la navigation par défaut + event?.preventDefault(); + // Check if the contract exists in mockContracts + const contract = mockContracts[contractId as keyof typeof mockContracts]; + if (!contract) { + console.error('Contract not found:', contractId); + return; + } + + // Créer la popup + const popup = document.createElement('div'); + popup.className = 'contract-popup-overlay'; + popup.innerHTML = ` +
+ +

${contract.title}

+
+

Date: ${contract.date}

+

Parties: ${contract.parties.join(', ')}

+

Terms:

+
    + ${contract.terms.map((term) => `
  • ${term}
  • `).join('')} +
+

Content: ${contract.content}

+
+
+ `; + + // Ajouter la popup au body + document.body.appendChild(popup); + + // Gérer la fermeture + const closeBtn = popup.querySelector('.close-contract-popup'); + const closePopup = () => popup.remove(); + + closeBtn?.addEventListener('click', closePopup); + popup.addEventListener('click', (e) => { + if (e.target === popup) closePopup(); + }); + } + + // Ajouter à l'objet window + + // Fonction utilitaire pour cacher tous les contenus + private 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 + private showPairing(): void { + isAddingRow = false; + currentRow = null; + + currentMode = 'pairing'; + // Cacher tous les contenus + this.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) || '[]'); + this.updateTableContent(rows); + } + } + + private showWallet(): void { + isAddingRow = false; + currentRow = null; + + currentMode = 'wallet'; + this.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'; + walletContent.innerHTML = ` +
Wallet
+
+ + + + + + + + + +
LabelWalletType
+
+ +
+
+ `; + + const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || '[]'); + this.updateWalletTableContent(rows); + } + + private 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(''); + } + + private showData(): void { + //console.log("showData called"); + currentMode = 'data'; + this.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) || '[]'); + this.updateDataTableContent(rows); + } + } + + // Fonctions de gestion du wallet + private 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 = ` +
+ + +
+ `; + + this.updateActionButtons(); + } + + private 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; + this.showWallet(); + } else { + showAlert('Please complete all fields before confirming.'); + } + } + + private 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 = ` + + `; + } + + private 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 + private openAvatarPopup(): void { + const popup = document.getElementById('avatar-popup'); + if (!popup) return; + + // Récupérer les valeurs stockées + const savedName = localStorage.getItem('userName'); + const savedLastName = localStorage.getItem('userLastName'); + 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'; + this.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'; + } + } + } + + private 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 = popup.querySelector('#popup-avatar-img') as HTMLImageElement; + const navAvatar = document.querySelector('.nav-wrapper .avatar') as HTMLImageElement; + const headerAvatar = document.querySelector('header-element')?.shadowRoot?.querySelector('.avatar') as HTMLImageElement; + + if (popupAvatar) popupAvatar.src = result; + if (navAvatar) navAvatar.src = result; + if (headerAvatar) headerAvatar.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 = popup.querySelector('#popup-banner-img') as HTMLImageElement; + const navBanner = document.querySelector('.nav-wrapper .banner-image') as HTMLImageElement; + const headerBanner = document.querySelector('header-element')?.shadowRoot?.querySelector('.banner-image') as HTMLImageElement; + + if (popupBanner) popupBanner.src = result; + if (navBanner) navBanner.src = result; + if (headerBanner) headerBanner.src = 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', () => { + const newName = nameInput.value; + localStorage.setItem('userName', newName); + + const headerName = document.querySelector('header-element')?.shadowRoot?.querySelector('.user-name'); + const navName = document.querySelector('.nav-wrapper .user-name'); + + if (headerName) headerName.textContent = newName; + if (navName) navName.textContent = newName; + }); + } + + if (lastNameInput) { + lastNameInput.addEventListener('input', () => { + const newLastName = lastNameInput.value; + console.log('Nom de famille mis à jour :', newLastName); + localStorage.setItem('userLastName', newLastName); + + const headerLastName = document.querySelector('header-element')?.shadowRoot?.querySelector('.user-lastname'); + const navLastName = document.querySelector('.nav-wrapper .user-lastname'); + + if (headerLastName) headerLastName.textContent = newLastName; + if (navLastName) navLastName.textContent = newLastName; + }); + } + } + + private closeAvatarPopup(): void { + const popup = document.querySelector('.avatar-popup'); + if (popup) popup.remove(); + } + + private loadAvatar(): void { + const savedAvatar = localStorage.getItem('userAvatar'); + if (savedAvatar) { + const avatarImg = document.querySelector('.avatar') as HTMLImageElement; + if (avatarImg) { + avatarImg.src = savedAvatar; + } + } + } + + private loadUserInfo(): void { + const savedName = localStorage.getItem('userName'); + const savedLastName = localStorage.getItem('userLastName'); + + console.log('Nom récupéré :', savedName); + console.log('Nom de famille récupéré :', savedLastName); + + 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; + } + } + + this.loadAvatar(); + this.loadSavedBanner(); + } + + private updateNavbarName(name: string): void { + const nameElement = document.querySelector('.nav-wrapper .user-name'); + if (nameElement) { + nameElement.textContent = name; + } + } + + private updateNavbarLastName(lastName: string): void { + const lastNameElement = document.querySelector('.nav-wrapper .user-lastname'); + if (lastNameElement) { + lastNameElement.textContent = lastName; + } + } + + private initializeEventListeners() { + document.addEventListener('DOMContentLoaded', () => { + this.showPairing(); + }); + + 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', this.handleAvatarUpload); + } + } + + connectedCallback() { + this.initializeEventListeners(); + this.loadSavedBanner(); + this.loadUserInfo(); + + const savedAvatar = localStorage.getItem('userAvatar'); + const savedBanner = localStorage.getItem('userBanner'); + const savedName = localStorage.getItem('userName'); + const savedLastName = localStorage.getItem('userLastName'); + + if (savedAvatar) { + const navAvatar = this.shadowRoot?.querySelector('.avatar') as HTMLImageElement; + if (navAvatar) navAvatar.src = savedAvatar; + } + + if (savedBanner) { + const navBanner = this.shadowRoot?.querySelector('.banner-image') as HTMLImageElement; + if (navBanner) navBanner.src = savedBanner; + } + + if (savedName) { + this.updateNavbarName(savedName); + } + if (savedLastName) { + this.updateNavbarLastName(savedLastName); + } + } +} + +customElements.define('account-element', AccountElement); +export { AccountElement }; diff --git a/src/pages/chat/chat-component.ts b/src/pages/chat/chat-component.ts index 3d533ed..ff409c6 100644 --- a/src/pages/chat/chat-component.ts +++ b/src/pages/chat/chat-component.ts @@ -1,59 +1,59 @@ -import { ChatElement } from './chat'; -import chatCss from '../../../public/style/chat.css?raw' -import Services from '../../services/service.js' - -class ChatComponent extends HTMLElement { - _callback: any - chatElement: ChatElement | null = null; - - constructor() { - super(); - console.log('INIT') - this.attachShadow({ mode: 'open' }); - - this.chatElement = this.shadowRoot?.querySelector('chat-element') || null; - } - - connectedCallback() { - console.log('CALLBACKs') - this.render(); - this.fetchData(); - - if (!customElements.get('chat-element')) { - customElements.define('chat-element', ChatElement); - } - } - - async fetchData() { - if ((import.meta as any).env.VITE_IS_INDEPENDANT_LIB === false) { - const data = await (window as any).myService?.getProcesses(); - } else { - const service = await Services.getInstance() - const data = await service.getProcesses(); - } - } - - set callback(fn) { - if (typeof fn === 'function') { - this._callback = fn; - } else { - console.error('Callback is not a function'); - } - } - - get callback() { - return this._callback; - } - - render() { - if(this.shadowRoot) { - // Créer l'élément chat-element - const chatElement = document.createElement('chat-element'); - this.shadowRoot.innerHTML = ``; - this.shadowRoot.appendChild(chatElement); - } - } -} - -export { ChatComponent } -customElements.define('chat-component', ChatComponent); +import { ChatElement } from './chat'; +import chatCss from '../../../public/style/chat.css?raw'; +import Services from '../../services/service.js'; + +class ChatComponent extends HTMLElement { + _callback: any; + chatElement: ChatElement | null = null; + + constructor() { + super(); + console.log('INIT'); + this.attachShadow({ mode: 'open' }); + + this.chatElement = this.shadowRoot?.querySelector('chat-element') || null; + } + + connectedCallback() { + console.log('CALLBACKs'); + this.render(); + this.fetchData(); + + if (!customElements.get('chat-element')) { + customElements.define('chat-element', ChatElement); + } + } + + async fetchData() { + if ((import.meta as any).env.VITE_IS_INDEPENDANT_LIB === false) { + const data = await (window as any).myService?.getProcesses(); + } else { + const service = await Services.getInstance(); + const data = await service.getProcesses(); + } + } + + set callback(fn) { + if (typeof fn === 'function') { + this._callback = fn; + } else { + console.error('Callback is not a function'); + } + } + + get callback() { + return this._callback; + } + + render() { + if (this.shadowRoot) { + // Créer l'élément chat-element + const chatElement = document.createElement('chat-element'); + this.shadowRoot.innerHTML = ``; + this.shadowRoot.appendChild(chatElement); + } + } +} + +export { ChatComponent }; +customElements.define('chat-component', ChatComponent); diff --git a/src/pages/chat/chat.html b/src/pages/chat/chat.html index 9d524e0..74c9b3b 100755 --- a/src/pages/chat/chat.html +++ b/src/pages/chat/chat.html @@ -1,53 +1,48 @@ - - - - - - - Messagerie - - - - - - -
- -
-
    - -
-
- - -
-
- -
-
- -
- - -
- - - - - - - -
-
- -
- - - - - \ No newline at end of file + + + + + + Messagerie + + + + + +
+ +
+
    + +
+
+ + +
+
+ +
+
+ +
+ + +
+ + + + + + +
+
+
+ + + diff --git a/src/pages/chat/chat.ts b/src/pages/chat/chat.ts index a641760..2639e9d 100755 --- a/src/pages/chat/chat.ts +++ b/src/pages/chat/chat.ts @@ -1,536 +1,527 @@ -declare global { - interface Window { - toggleUserList: () => void; - switchUser: (userId: string | number) => void; - loadMemberChat: (memberId: string | number) => void; - } -} - -import { groupsMock } from '../../mocks/mock-signature/groupsMock'; -import { messagesMock as initialMessagesMock, messagesMock } from '../../mocks/mock-signature/messagesMock'; -import { membersMock } from '../../mocks/mock-signature/membersMocks'; -import { - Message, - DocumentSignature, - } from '../../models/signature.models'; -import { messageStore } from '../../utils/messageMock'; -import { Member } from '../../interface/memberInterface'; -import { Group } from '../../interface/groupInterface'; -import { getCorrectDOM } from '../../utils/document.utils'; - - -let currentUser: Member = membersMock[0]; - -interface LocalNotification { - memberId: string; - text: string; - time: string; -} - -export function initChat() { - const chatElement = document.createElement('chat-element'); - const container = document.querySelector('.container'); - if (container) { - container.appendChild(chatElement); - } -} - -class ChatElement extends HTMLElement { - private selectedMemberId: string | null = null; - private messagesMock: any[] = []; - private dom: Node; - private notifications: LocalNotification[] = []; - private notificationBadge = document.querySelector('.notification-badge'); - private notificationBoard = document.getElementById('notification-board'); - private notificationBell = document.getElementById('notification-bell'); - private selectedSignatories: DocumentSignature[] = []; - private allMembers = membersMock.map(member => ({ - id: member.id, - name: member.name, - roleName: 'Default Role' - })); - - - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - this.dom = getCorrectDOM('signature-element'); - - window.toggleUserList = this.toggleUserList.bind(this); - window.switchUser = this.switchUser.bind(this); - window.loadMemberChat = this.loadMemberChat.bind(this); - - // Initialiser les événements de notification - document.addEventListener('click', (event: Event): void => { - if (this.notificationBoard && this.notificationBoard.style.display === 'block' && - !this.notificationBoard.contains(event.target as Node) && - this.notificationBell && !this.notificationBell.contains(event.target as Node)) { - this.notificationBoard.style.display = 'none'; - } - }); - this.initMessageEvents(); - this.initFileUpload(); - } - - private initMessageEvents() { - // Pour le bouton Send - const sendButton = document.getElementById('send-button'); - if (sendButton) { - sendButton.addEventListener('click', () => this.sendMessage()); - } - - // Pour la touche Entrée - const messageInput = document.getElementById('message-input'); - if (messageInput) { - messageInput.addEventListener('keypress', (event: KeyboardEvent) => { - if (event.key === 'Enter' && !event.shiftKey) { - event.preventDefault(); - this.sendMessage(); - } - }); - } - } - - private initFileUpload() { - const fileInput = document.getElementById('file-input') as HTMLInputElement; - if (fileInput) { - fileInput.addEventListener('change', (event: Event) => { - const target = event.target as HTMLInputElement; - if (target.files && target.files.length > 0) { - this.sendFile(target.files[0]); - } - }); - } - } - - ///////////////////// Notification module ///////////////////// - // Delete a notification - private removeNotification(index: number) { - this.notifications?.splice(index, 1); // Ajout de ?. - this.renderNotifications(); - this.updateNotificationBadge(); - } - // Show notifications - private renderNotifications() { - if (!this.notificationBoard) return; - - // Reset the interface - this.notificationBoard.innerHTML = ''; - - // Displays "No notifications available" if there are no notifications - if (this.notifications.length === 0) { - this.notificationBoard.innerHTML = '
No notifications available
'; - return; - } - - // Add each notification to the list - this.notifications.forEach((notif, index) => { - const notifElement = document.createElement('div'); - notifElement.className = 'notification-item'; - notifElement.textContent = `${notif.text} at ${notif.time}`; - notifElement.onclick = () => { - this.loadMemberChat(notif.memberId); - this.removeNotification(index); - }; - this.notificationBoard?.appendChild(notifElement); - }); - } - private updateNotificationBadge() { - if (!this.notificationBadge) return; - const count = this.notifications.length; - this.notificationBadge.textContent = count > 99 ? '+99' : count.toString(); - (this.notificationBadge as HTMLElement).style.display = count > 0 ? 'block' : 'none'; - } - - - // Add notification - private 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 - this.notifications.push(notification); - this.renderNotifications(); - this.updateNotificationBadge(); - } - -// Send a messsage - private sendMessage() { - const messageInput = document.getElementById('message-input') as HTMLInputElement; - if (!messageInput) return; - const messageText = messageInput.value.trim(); - - if (messageText === '' || this.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 - }; - // Add and display the message immediately - messageStore.addMessage(this.selectedMemberId, newMessage); - this.messagesMock = messageStore.getMessages(); - this.loadMemberChat(this.selectedMemberId); - - // Reset the input - messageInput.value = ''; - - // Automatic response after 2 seconds - setTimeout(() => { - if (this.selectedMemberId) { - const autoReply = this.generateAutoReply(`Member ${this.selectedMemberId}`); - messageStore.addMessage(this.selectedMemberId, autoReply); - this.messagesMock = messageStore.getMessages(); - this.loadMemberChat(this.selectedMemberId); - this.addNotification(this.selectedMemberId, autoReply); - } - }, 2000); - } - - // Scroll down the conversation after loading messages - private scrollToBottom(container: HTMLElement) { - container.scrollTop = container.scrollHeight; - } - - - // Load the list of members - private loadMemberChat(memberId: string | number) { - this.selectedMemberId = String(memberId); - const memberMessages = this.messagesMock.find(m => String(m.memberId) === String(memberId)); - - // Find the process and the role of the member - 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); - }); - } - - - this.scrollToBottom(messagesContainer); - } - - private 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(); - this.loadMemberChat(member.id.toString()); - }; - - memberList.appendChild(memberItem); - }); - - roleElement.appendChild(memberList); - } - - - // Toggle the list of Roles - private toggleRoles(group: Group, groupElement: HTMLElement) { - console.log('=== toggleRoles START ==='); - console.log('Group:', group.name); - console.log('Group roles:', group.roles); // Afficher tous les rôles disponibles - - let roleList = groupElement.querySelector('.role-list'); - console.log('Existing roleList:', roleList); - - if (roleList) { - const roleItems = roleList.querySelectorAll('.role-item'); - roleItems.forEach(roleItem => { - console.log('Processing roleItem:', roleItem.innerHTML); // Voir le contenu HTML complet - - let container = roleItem.querySelector('.role-item-container'); - if (!container) { - container = document.createElement('div'); - container.className = 'role-item-container'; - - // Créer un span pour le nom du rôle - const nameSpan = document.createElement('span'); - nameSpan.className = 'role-name'; - nameSpan.textContent = roleItem.textContent?.trim() || ''; - - container.appendChild(nameSpan); - roleItem.textContent = ''; - roleItem.appendChild(container); - } - - // Récupérer le nom du rôle - const roleName = roleItem.textContent?.trim(); - console.log('Role name from textContent:', roleName); - - // Alternative pour obtenir le nom du rôle - const roleNameAlt = container.querySelector('.role-name')?.textContent; - console.log('Role name from span:', roleNameAlt); - - if (!container.querySelector('.folder-icon')) { - const folderButton = document.createElement('span'); - folderButton.className = 'folder-icon'; - - folderButton.addEventListener('click', (event) => { - event.stopPropagation(); - console.log('Clicked role name:', roleName); - console.log('Available roles:', group.roles.map(r => r.name)); - - const role = group.roles.find(r => r.name === roleName); - if (role) { - console.log('Found role:', role); - } else { - console.error('Role not found. Name:', roleName); - console.error('Available roles:', group.roles); - } - }); - - container.appendChild(folderButton); - } - }); - - (roleList as HTMLElement).style.display = - (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none'; - } - } - - - private loadGroupList(): void { - const groupList = document.getElementById('group-list'); - if (!groupList) return; - - groupsMock.forEach(group => { - const li = document.createElement('li'); - li.className = 'group-list-item'; - - // Create a flex container for the name and the icon - const container = document.createElement('div'); - container.className = 'group-item-container'; - - // Span for the process name - const nameSpan = document.createElement('span'); - nameSpan.textContent = group.name; - nameSpan.className = 'process-name'; - - // Add click event to show roles - nameSpan.addEventListener('click', (event) => { - event.stopPropagation(); - this.toggleRoles(group, li); - }); - - // Assemble the elements - container.appendChild(nameSpan); - li.appendChild(container); - - // Create and append the role list container - const roleList = document.createElement('ul'); - roleList.className = 'role-list'; - roleList.style.display = 'none'; - - // Add roles for this process - group.roles.forEach(role => { - const roleItem = document.createElement('li'); - roleItem.className = 'role-item'; - roleItem.textContent = role.name; - roleItem.onclick = (event) => { - event.stopPropagation(); - this.toggleMembers(role, roleItem); - }; - roleList.appendChild(roleItem); - }); - - li.appendChild(roleList); - groupList.appendChild(li); - }); - } - - - // Function to manage the list of users - private toggleUserList() { - const userList = getCorrectDOM('userList'); - if (!userList) return; - - if (!(userList as HTMLElement).classList.contains('show')) { - (userList as HTMLElement).innerHTML = membersMock.map(member => ` -
- ${member.avatar} -
- ${member.name} - ${member.email} -
-
- `).join(''); - } - (userList as HTMLElement).classList.toggle('show'); - } - - private switchUser(userId: string | number) { - const user = membersMock.find(member => member.id === userId); - if (!user) return; - currentUser = user; - this.updateCurrentUserDisplay(); - const userList = getCorrectDOM('userList') as HTMLElement; - userList?.classList.remove('show'); - } - - // Function to update the display of the current user - private updateCurrentUserDisplay() { - const userDisplay = getCorrectDOM('current-user') as HTMLElement; - if (userDisplay) { - userDisplay.innerHTML = ` - - `; - } - } - // Generate an automatic response - private 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 file - private sendFile(file: File) { - console.log('SendFile called with file:', file); - const reader = new FileReader(); - reader.onloadend = () => { - const fileData = reader.result; - const fileName = file.name; - console.log('File loaded:', fileName); - - if (this.selectedMemberId) { - messageStore.addMessage(this.selectedMemberId, { - id: Date.now(), - sender: "4NK", - fileName: fileName, - fileData: fileData, - time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), - type: 'file' - }); - console.log('Message added to store'); - - this.messagesMock = messageStore.getMessages(); - this.loadMemberChat(this.selectedMemberId); - } - }; - reader.readAsDataURL(file); - } - - private initializeEventListeners() { - document.addEventListener('DOMContentLoaded', (): void => { - }); - - // Gestionnaire d'événements pour le chat - const sendBtn = this.shadowRoot?.querySelector('#send-button'); - if (sendBtn) { - sendBtn.addEventListener('click', this.sendMessage.bind(this)); - } - - const messageInput = this.shadowRoot?.querySelector('#message-input'); - if (messageInput) { - messageInput.addEventListener('keypress', (event: Event) => { - if ((event as KeyboardEvent).key === 'Enter') { - event.preventDefault(); - this.sendMessage(); - } - }); - } - - // Gestionnaire pour l'envoi de fichiers - const fileInput = this.shadowRoot?.querySelector('#file-input'); - if (fileInput) { - fileInput.addEventListener('change', (event: Event) => { - const file = (event.target as HTMLInputElement).files?.[0]; - if (file) { - this.sendFile(file); - } - }); - } - } - - connectedCallback() { - if (this.shadowRoot) { - this.shadowRoot.innerHTML = ` -
-
-
    -
    -
    - -
    -
    - `; - } - - this.messagesMock = messageStore.getMessages(); - if (this.messagesMock.length === 0) { - messageStore.setMessages(initialMessagesMock); - this.messagesMock = messageStore.getMessages(); - } - this.updateCurrentUserDisplay(); - this.initializeEventListeners(); - this.loadGroupList(); - } -} - -customElements.define('chat-element', ChatElement); -export { ChatElement }; - +declare global { + interface Window { + toggleUserList: () => void; + switchUser: (userId: string | number) => void; + loadMemberChat: (memberId: string | number) => void; + } +} + +import { groupsMock } from '../../mocks/mock-signature/groupsMock'; +import { messagesMock as initialMessagesMock, messagesMock } from '../../mocks/mock-signature/messagesMock'; +import { membersMock } from '../../mocks/mock-signature/membersMocks'; +import { Message, DocumentSignature } from '../../models/signature.models'; +import { messageStore } from '../../utils/messageMock'; +import { Member } from '../../interface/memberInterface'; +import { Group } from '../../interface/groupInterface'; +import { getCorrectDOM } from '../../utils/document.utils'; + +let currentUser: Member = membersMock[0]; + +interface LocalNotification { + memberId: string; + text: string; + time: string; +} + +export function initChat() { + const chatElement = document.createElement('chat-element'); + const container = document.querySelector('.container'); + if (container) { + container.appendChild(chatElement); + } +} + +class ChatElement extends HTMLElement { + private selectedMemberId: string | null = null; + private messagesMock: any[] = []; + private dom: Node; + private notifications: LocalNotification[] = []; + private notificationBadge = document.querySelector('.notification-badge'); + private notificationBoard = document.getElementById('notification-board'); + private notificationBell = document.getElementById('notification-bell'); + private selectedSignatories: DocumentSignature[] = []; + private allMembers = membersMock.map((member) => ({ + id: member.id, + name: member.name, + roleName: 'Default Role', + })); + + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this.dom = getCorrectDOM('signature-element'); + + window.toggleUserList = this.toggleUserList.bind(this); + window.switchUser = this.switchUser.bind(this); + window.loadMemberChat = this.loadMemberChat.bind(this); + + // Initialiser les événements de notification + document.addEventListener('click', (event: Event): void => { + if (this.notificationBoard && this.notificationBoard.style.display === 'block' && !this.notificationBoard.contains(event.target as Node) && this.notificationBell && !this.notificationBell.contains(event.target as Node)) { + this.notificationBoard.style.display = 'none'; + } + }); + this.initMessageEvents(); + this.initFileUpload(); + } + + private initMessageEvents() { + // Pour le bouton Send + const sendButton = document.getElementById('send-button'); + if (sendButton) { + sendButton.addEventListener('click', () => this.sendMessage()); + } + + // Pour la touche Entrée + const messageInput = document.getElementById('message-input'); + if (messageInput) { + messageInput.addEventListener('keypress', (event: KeyboardEvent) => { + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); + this.sendMessage(); + } + }); + } + } + + private initFileUpload() { + const fileInput = document.getElementById('file-input') as HTMLInputElement; + if (fileInput) { + fileInput.addEventListener('change', (event: Event) => { + const target = event.target as HTMLInputElement; + if (target.files && target.files.length > 0) { + this.sendFile(target.files[0]); + } + }); + } + } + + ///////////////////// Notification module ///////////////////// + // Delete a notification + private removeNotification(index: number) { + this.notifications?.splice(index, 1); // Ajout de ?. + this.renderNotifications(); + this.updateNotificationBadge(); + } + // Show notifications + private renderNotifications() { + if (!this.notificationBoard) return; + + // Reset the interface + this.notificationBoard.innerHTML = ''; + + // Displays "No notifications available" if there are no notifications + if (this.notifications.length === 0) { + this.notificationBoard.innerHTML = '
    No notifications available
    '; + return; + } + + // Add each notification to the list + this.notifications.forEach((notif, index) => { + const notifElement = document.createElement('div'); + notifElement.className = 'notification-item'; + notifElement.textContent = `${notif.text} at ${notif.time}`; + notifElement.onclick = () => { + this.loadMemberChat(notif.memberId); + this.removeNotification(index); + }; + this.notificationBoard?.appendChild(notifElement); + }); + } + private updateNotificationBadge() { + if (!this.notificationBadge) return; + const count = this.notifications.length; + this.notificationBadge.textContent = count > 99 ? '+99' : count.toString(); + (this.notificationBadge as HTMLElement).style.display = count > 0 ? 'block' : 'none'; + } + + // Add notification + private 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 + this.notifications.push(notification); + this.renderNotifications(); + this.updateNotificationBadge(); + } + + // Send a messsage + private sendMessage() { + const messageInput = document.getElementById('message-input') as HTMLInputElement; + if (!messageInput) return; + const messageText = messageInput.value.trim(); + + if (messageText === '' || this.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, + }; + // Add and display the message immediately + messageStore.addMessage(this.selectedMemberId, newMessage); + this.messagesMock = messageStore.getMessages(); + this.loadMemberChat(this.selectedMemberId); + + // Reset the input + messageInput.value = ''; + + // Automatic response after 2 seconds + setTimeout(() => { + if (this.selectedMemberId) { + const autoReply = this.generateAutoReply(`Member ${this.selectedMemberId}`); + messageStore.addMessage(this.selectedMemberId, autoReply); + this.messagesMock = messageStore.getMessages(); + this.loadMemberChat(this.selectedMemberId); + this.addNotification(this.selectedMemberId, autoReply); + } + }, 2000); + } + + // Scroll down the conversation after loading messages + private scrollToBottom(container: HTMLElement) { + container.scrollTop = container.scrollHeight; + } + + // Load the list of members + private loadMemberChat(memberId: string | number) { + this.selectedMemberId = String(memberId); + const memberMessages = this.messagesMock.find((m) => String(m.memberId) === String(memberId)); + + // Find the process and the role of the member + 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); + }); + } + + this.scrollToBottom(messagesContainer); + } + + private 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(); + this.loadMemberChat(member.id.toString()); + }; + + memberList.appendChild(memberItem); + }); + + roleElement.appendChild(memberList); + } + + // Toggle the list of Roles + private toggleRoles(group: Group, groupElement: HTMLElement) { + console.log('=== toggleRoles START ==='); + console.log('Group:', group.name); + console.log('Group roles:', group.roles); // Afficher tous les rôles disponibles + + let roleList = groupElement.querySelector('.role-list'); + console.log('Existing roleList:', roleList); + + if (roleList) { + const roleItems = roleList.querySelectorAll('.role-item'); + roleItems.forEach((roleItem) => { + console.log('Processing roleItem:', roleItem.innerHTML); // Voir le contenu HTML complet + + let container = roleItem.querySelector('.role-item-container'); + if (!container) { + container = document.createElement('div'); + container.className = 'role-item-container'; + + // Créer un span pour le nom du rôle + const nameSpan = document.createElement('span'); + nameSpan.className = 'role-name'; + nameSpan.textContent = roleItem.textContent?.trim() || ''; + + container.appendChild(nameSpan); + roleItem.textContent = ''; + roleItem.appendChild(container); + } + + // Récupérer le nom du rôle + const roleName = roleItem.textContent?.trim(); + console.log('Role name from textContent:', roleName); + + // Alternative pour obtenir le nom du rôle + const roleNameAlt = container.querySelector('.role-name')?.textContent; + console.log('Role name from span:', roleNameAlt); + + if (!container.querySelector('.folder-icon')) { + const folderButton = document.createElement('span'); + folderButton.className = 'folder-icon'; + + folderButton.addEventListener('click', (event) => { + event.stopPropagation(); + console.log('Clicked role name:', roleName); + console.log( + 'Available roles:', + group.roles.map((r) => r.name), + ); + + const role = group.roles.find((r) => r.name === roleName); + if (role) { + console.log('Found role:', role); + } else { + console.error('Role not found. Name:', roleName); + console.error('Available roles:', group.roles); + } + }); + + container.appendChild(folderButton); + } + }); + + (roleList as HTMLElement).style.display = (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none'; + } + } + + private loadGroupList(): void { + const groupList = document.getElementById('group-list'); + if (!groupList) return; + + groupsMock.forEach((group) => { + const li = document.createElement('li'); + li.className = 'group-list-item'; + + // Create a flex container for the name and the icon + const container = document.createElement('div'); + container.className = 'group-item-container'; + + // Span for the process name + const nameSpan = document.createElement('span'); + nameSpan.textContent = group.name; + nameSpan.className = 'process-name'; + + // Add click event to show roles + nameSpan.addEventListener('click', (event) => { + event.stopPropagation(); + this.toggleRoles(group, li); + }); + + // Assemble the elements + container.appendChild(nameSpan); + li.appendChild(container); + + // Create and append the role list container + const roleList = document.createElement('ul'); + roleList.className = 'role-list'; + roleList.style.display = 'none'; + + // Add roles for this process + group.roles.forEach((role) => { + const roleItem = document.createElement('li'); + roleItem.className = 'role-item'; + roleItem.textContent = role.name; + roleItem.onclick = (event) => { + event.stopPropagation(); + this.toggleMembers(role, roleItem); + }; + roleList.appendChild(roleItem); + }); + + li.appendChild(roleList); + groupList.appendChild(li); + }); + } + + // Function to manage the list of users + private toggleUserList() { + const userList = getCorrectDOM('userList'); + if (!userList) return; + + if (!(userList as HTMLElement).classList.contains('show')) { + (userList as HTMLElement).innerHTML = membersMock + .map( + (member) => ` +
    + ${member.avatar} +
    + ${member.name} + ${member.email} +
    +
    + `, + ) + .join(''); + } + (userList as HTMLElement).classList.toggle('show'); + } + + private switchUser(userId: string | number) { + const user = membersMock.find((member) => member.id === userId); + if (!user) return; + currentUser = user; + this.updateCurrentUserDisplay(); + const userList = getCorrectDOM('userList') as HTMLElement; + userList?.classList.remove('show'); + } + + // Function to update the display of the current user + private updateCurrentUserDisplay() { + const userDisplay = getCorrectDOM('current-user') as HTMLElement; + if (userDisplay) { + userDisplay.innerHTML = ` + + `; + } + } + // Generate an automatic response + private 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 file + private sendFile(file: File) { + console.log('SendFile called with file:', file); + const reader = new FileReader(); + reader.onloadend = () => { + const fileData = reader.result; + const fileName = file.name; + console.log('File loaded:', fileName); + + if (this.selectedMemberId) { + messageStore.addMessage(this.selectedMemberId, { + id: Date.now(), + sender: '4NK', + fileName: fileName, + fileData: fileData, + time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + type: 'file', + }); + console.log('Message added to store'); + + this.messagesMock = messageStore.getMessages(); + this.loadMemberChat(this.selectedMemberId); + } + }; + reader.readAsDataURL(file); + } + + private initializeEventListeners() { + document.addEventListener('DOMContentLoaded', (): void => {}); + + // Gestionnaire d'événements pour le chat + const sendBtn = this.shadowRoot?.querySelector('#send-button'); + if (sendBtn) { + sendBtn.addEventListener('click', this.sendMessage.bind(this)); + } + + const messageInput = this.shadowRoot?.querySelector('#message-input'); + if (messageInput) { + messageInput.addEventListener('keypress', (event: Event) => { + if ((event as KeyboardEvent).key === 'Enter') { + event.preventDefault(); + this.sendMessage(); + } + }); + } + + // Gestionnaire pour l'envoi de fichiers + const fileInput = this.shadowRoot?.querySelector('#file-input'); + if (fileInput) { + fileInput.addEventListener('change', (event: Event) => { + const file = (event.target as HTMLInputElement).files?.[0]; + if (file) { + this.sendFile(file); + } + }); + } + } + + connectedCallback() { + if (this.shadowRoot) { + this.shadowRoot.innerHTML = ` +
    +
    +
      +
      +
      + +
      +
      + `; + } + + this.messagesMock = messageStore.getMessages(); + if (this.messagesMock.length === 0) { + messageStore.setMessages(initialMessagesMock); + this.messagesMock = messageStore.getMessages(); + } + this.updateCurrentUserDisplay(); + this.initializeEventListeners(); + this.loadGroupList(); + } +} + +customElements.define('chat-element', ChatElement); +export { ChatElement }; diff --git a/src/pages/home/home-component.ts b/src/pages/home/home-component.ts index 94c8605..b38e1a5 100644 --- a/src/pages/home/home-component.ts +++ b/src/pages/home/home-component.ts @@ -1,48 +1,49 @@ -import loginHtml from './home.html?raw' -import loginScript from './home.ts?raw' -import loginCss from '../../4nk.css?raw' -import { initHomePage } from './home'; - -export class LoginComponent extends HTMLElement { - _callback: any; - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - } - - connectedCallback() { - console.log('CALLBACK LOGIN PAGE') - this.render(); - setTimeout(() => { - initHomePage(); - }, 500); - } - - set callback(fn) { - if (typeof fn === 'function') { - this._callback = fn; - } else { - console.error('Callback is not a function'); - } - } - - get callback() { - return this._callback; - } - - render() { - if(this.shadowRoot) this.shadowRoot.innerHTML = ` - ${loginHtml} - - - - - \ No newline at end of file + + + + + + Signatures + + + + + +
      + +
      +
        +
        + + +
        +
        + +
        +
        + +
        + + +
        + + + + + + +
        +
        +
        + + + diff --git a/src/pages/signature/signature.ts b/src/pages/signature/signature.ts index 8716364..6e0a4fa 100755 --- a/src/pages/signature/signature.ts +++ b/src/pages/signature/signature.ts @@ -1,1711 +1,1716 @@ -declare global { - interface Window { - toggleUserList: () => void; - switchUser: (userId: string | number) => void; - closeProcessDetails: (groupId: number) => void; - loadMemberChat: (memberId: string | number) => void; - closeRoleDocuments: (roleName: string) => void; - newRequest: (params: RequestParams) => void; - submitRequest: () => void; - closeNewRequest: () => void; - closeModal: (button: HTMLElement) => void; - submitDocumentRequest: (documentId: number) => void; - submitNewDocument: (event: Event) => void; - submitCommonDocument: (event: Event) => void; - signDocument: (documentId: number, processId: number, isCommonDocument: boolean) => void; - confirmSignature: (documentId: number, processId: number, isCommonDocument: boolean) => void; - } -} - -import { groupsMock } from '../../mocks/mock-signature/groupsMock'; -import { messagesMock as initialMessagesMock, messagesMock } from '../../mocks/mock-signature/messagesMock'; -import { membersMock } from '../../mocks/mock-signature/membersMocks'; -import { - Message, - DocumentSignature, - RequestParams} from '../../models/signature.models'; -import { messageStore } from '../../utils/messageMock'; -import { showAlert } from '../account/account'; -import { Member } from '../../interface/memberInterface'; -import { Group } from '../../interface/groupInterface'; -import { getCorrectDOM } from '../../utils/document.utils'; - - -let currentUser: Member = membersMock[0]; - -interface LocalNotification { - memberId: string; - text: string; - time: string; -} - - -class SignatureElement extends HTMLElement { - private selectedMemberId: string | null = null; - private messagesMock: any[] = []; - private dom: Node; - private notifications: LocalNotification[] = []; - private notificationBadge = document.querySelector('.notification-badge'); - private notificationBoard = document.getElementById('notification-board'); - private notificationBell = document.getElementById('notification-bell'); - private selectedSignatories: DocumentSignature[] = []; - private allMembers = membersMock.map(member => ({ - id: member.id, - name: member.name, - roleName: 'Default Role' - })); - - private signDocument(documentId: number, processId: number, isCommonDocument: boolean = false): void { - if (typeof window === 'undefined' || typeof document === 'undefined') { - console.error('Cette fonction ne peut être exécutée que dans un navigateur'); - return; - } - - try { - const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock)); - const group = groups.find((g: Group) => g.id === processId); - - if (!group) { - throw new Error('Process not found'); - } - - let targetDoc; - if (isCommonDocument) { - targetDoc = group.commonDocuments.find((d: any) => d.id === documentId); - } else { - for (const role of group.roles) { - if (role.documents) { - targetDoc = role.documents.find((d: any) => d.id === documentId); - if (targetDoc) break; - } - } - } - - if (!targetDoc) { - throw new Error('Document not found'); - } - - const canSign = isCommonDocument ? - targetDoc.signatures?.some((sig: DocumentSignature) => - sig.member?.name === currentUser?.name && !sig.signed - ) : - this.canUserSignDocument(targetDoc, currentUser?.name, currentUser); - - if (!canSign) { - showAlert("You do not have the necessary rights to sign this document."); - return; - } - - // Create and insert the modal directly into the body - const modalHtml = ` - `; - - document.body.insertAdjacentHTML('beforeend', modalHtml); - - // Add the slider logic after creating the modal - const slider = document.getElementById('signatureSlider'); - const sliderTrack = slider?.parentElement; - let isDragging = false; - let startX: number; - let sliderLeft: number; - - if (slider && sliderTrack) { - slider.addEventListener('mousedown', initDrag.bind(this)); - document.addEventListener('mousemove', drag.bind(this)); - document.addEventListener('mouseup', stopDrag.bind(this)); - - // Touch events for mobile - slider.addEventListener('touchstart', initDrag.bind(this)); - document.addEventListener('touchmove', drag.bind(this)); - document.addEventListener('touchend', stopDrag.bind(this)); - } - - function initDrag(e: MouseEvent | TouchEvent) { - isDragging = true; - startX = 'touches' in e ? e.touches[0].clientX : e.clientX; - sliderLeft = slider?.offsetLeft || 0; - } - - function drag(this: SignatureElement, e: MouseEvent | TouchEvent) { - if (!isDragging || !slider || !sliderTrack) return; - - e.preventDefault(); - const rect = sliderTrack.getBoundingClientRect(); - const x = 'touches' in e ? e.touches[0].clientX : e.clientX; - - - - // Calculate the position relative to the track - let newLeft = x - rect.left - (slider.offsetWidth / 2); - - // Limit the movement - const maxLeft = sliderTrack.offsetWidth - slider.offsetWidth; - newLeft = Math.max(0, Math.min(newLeft, maxLeft)); - - // Update the position - slider.style.left = `${newLeft}px`; - - // If the slider reaches 90% of the path, trigger the signature - if (newLeft > maxLeft * 0.9) { - stopDrag(e); - this.confirmSignature(documentId, processId, isCommonDocument); - } - } - - function stopDrag(e: MouseEvent | TouchEvent) { - if (!isDragging || !slider) return; - isDragging = false; - - // Reset the position if not enough dragged - if (slider.offsetLeft < (sliderTrack?.offsetWidth || 0) * 0.9) { - slider.style.left = '0px'; - } - } - - } catch (error) { - console.error('Error displaying modal:', error); - showAlert(error instanceof Error ? error.message : 'Error displaying modal'); - } - } - - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - this.dom = getCorrectDOM('signature-element'); - - window.toggleUserList = this.toggleUserList.bind(this); - window.switchUser = this.switchUser.bind(this); - window.closeProcessDetails = this.closeProcessDetails.bind(this); - window.loadMemberChat = this.loadMemberChat.bind(this); - window.closeRoleDocuments = this.closeRoleDocuments.bind(this); - window.newRequest = this.newRequest.bind(this); - window.submitRequest = this.submitRequest.bind(this); - window.closeNewRequest = this.closeNewRequest.bind(this); - window.closeModal = this.closeModal.bind(this); - window.submitNewDocument = this.submitNewDocument.bind(this); - window.submitCommonDocument = this.submitCommonDocument.bind(this); - window.signDocument = this.signDocument.bind(this); - window.confirmSignature = this.confirmSignature.bind(this); - window.submitDocumentRequest = this.submitDocumentRequest.bind(this); - - // Initialiser les événements de notification - document.addEventListener('click', (event: Event): void => { - if (this.notificationBoard && this.notificationBoard.style.display === 'block' && - !this.notificationBoard.contains(event.target as Node) && - this.notificationBell && !this.notificationBell.contains(event.target as Node)) { - this.notificationBoard.style.display = 'none'; - } - }); - this.initMessageEvents(); - this.initFileUpload(); - } - - private initMessageEvents() { - // Pour le bouton Send - const sendButton = document.getElementById('send-button'); - if (sendButton) { - sendButton.addEventListener('click', () => this.sendMessage()); - } - - // Pour la touche Entrée - const messageInput = document.getElementById('message-input'); - if (messageInput) { - messageInput.addEventListener('keypress', (event: KeyboardEvent) => { - if (event.key === 'Enter' && !event.shiftKey) { - event.preventDefault(); - this.sendMessage(); - } - }); - } - } - - private initFileUpload() { - const fileInput = document.getElementById('file-input') as HTMLInputElement; - if (fileInput) { - fileInput.addEventListener('change', (event: Event) => { - const target = event.target as HTMLInputElement; - if (target.files && target.files.length > 0) { - this.sendFile(target.files[0]); - } - }); - } - } - - - private 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)); - } - - // Add this helper function - private canUserAccessDocument(document: any, roleId: string, currentUserRole: string): boolean { - // Modify the access logic - if (document.visibility === 'public') { - return true; // Can see but not necessarily sign - } - return roleId === currentUserRole; - } - - private canUserSignDocument(document: any, role: string, user: Member): boolean { - console.log('Checking signing rights for:', { - document, - role, - user, - userRoles: user.processRoles - }); - - // Vérifier si l'utilisateur est dans la liste des signatures - const isSignatory = document.signatures?.some((sig: DocumentSignature) => - sig.member && 'id' in sig.member && sig.member.id === user.id && !sig.signed - ); - - if (!isSignatory) { - console.log('User is not in signatures list or has already signed'); - return false; - } - - // Si l'utilisateur est dans la liste des signatures, il peut signer - return true; - } - - private 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'; - } - } - - ///////////////////// Notification module ///////////////////// - // Delete a notification - private removeNotification(index: number) { - this.notifications?.splice(index, 1); // Ajout de ?. - this.renderNotifications(); - this.updateNotificationBadge(); - } - // Show notifications - private renderNotifications() { - if (!this.notificationBoard) return; - - // Reset the interface - this.notificationBoard.innerHTML = ''; - - // Displays "No notifications available" if there are no notifications - if (this.notifications.length === 0) { - this.notificationBoard.innerHTML = '
        No notifications available
        '; - return; - } - - // Add each notification to the list - this.notifications.forEach((notif, index) => { - const notifElement = document.createElement('div'); - notifElement.className = 'notification-item'; - notifElement.textContent = `${notif.text} at ${notif.time}`; - notifElement.onclick = () => { - this.loadMemberChat(notif.memberId); - this.removeNotification(index); - }; - this.notificationBoard?.appendChild(notifElement); - }); - } - private updateNotificationBadge() { - if (!this.notificationBadge) return; - const count = this.notifications.length; - this.notificationBadge.textContent = count > 99 ? '+99' : count.toString(); - (this.notificationBadge as HTMLElement).style.display = count > 0 ? 'block' : 'none'; - } - - - // Add notification - private 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 - this.notifications.push(notification); - this.renderNotifications(); - this.updateNotificationBadge(); - } -// Send a messsage - private sendMessage() { - const messageInput = document.getElementById('message-input') as HTMLInputElement; - if (!messageInput) return; - const messageText = messageInput.value.trim(); - - if (messageText === '' || this.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 - }; - // Add and display the message immediately - messageStore.addMessage(this.selectedMemberId, newMessage); - this.messagesMock = messageStore.getMessages(); - this.loadMemberChat(this.selectedMemberId); - - // Reset the input - messageInput.value = ''; - - // Automatic response after 2 seconds - setTimeout(() => { - if (this.selectedMemberId) { - const autoReply = this.generateAutoReply(`Member ${this.selectedMemberId}`); - messageStore.addMessage(this.selectedMemberId, autoReply); - this.messagesMock = messageStore.getMessages(); - this.loadMemberChat(this.selectedMemberId); - this.addNotification(this.selectedMemberId, autoReply); - } - }, 2000); -} - - - private showProcessDetails(group: Group, groupId: number) { - console.log('Showing details for group:', groupId); - - // Close all existing process views - 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; - } - - // Load the data from localStorage - const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]'); - const storedGroup = storedGroups.find((g: Group) => g.id === groupId); - - // Use the data from localStorage if available, otherwise use the group passed as a parameter - const displayGroup = storedGroup || group; - - 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 = ` -
        -

        ${displayGroup.name}

        -
        -
        -
        -
        -
        -

        Description

        -

        ${displayGroup.description || 'No description available'}

        -
        -
        -

        Documents Communs

        -
        - ${displayGroup.commonDocuments.map((document: any) => { - const totalSignatures = document.signatures?.length || 0; - const signedCount = document.signatures?.filter((sig: DocumentSignature) => sig.signed).length || 0; - const percentage = totalSignatures > 0 ? (signedCount / totalSignatures) * 100 : 0; - const isVierge = !document.createdAt || !document.deadline || !document.signatures?.length; - const canSign = document.signatures?.some((sig: DocumentSignature) => - sig.member && 'id' in sig.member && sig.member.id === currentUser.id && !sig.signed - ); - - const signButton = !isVierge ? ` - ${totalSignatures > 0 && signedCount < totalSignatures && canSign ? ` - - ` : ''} - ` : ''; - - 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: ${this.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 - Waiting for creation

        - - `} - ${signButton} -
        -
        - `; - }).join('')} -
        -
        -
        -

        Roles and Documents

        - ${displayGroup.roles.map((role: { name: string; documents?: any[] }) => { - // Filter the documents according to the access rights - const accessibleDocuments = (role.documents || []).filter(doc => - this.canUserAccessDocument(doc, role.name, currentUser.processRoles?.[0]?.role || '') - ); - - return ` -
        -

        ${role.name}

        -
        - ${accessibleDocuments.map(document => { - const isVierge = !document.createdAt || - !document.deadline || - document.signatures.length === 0; - - const canSign = this.canUserSignDocument(document, role.name, currentUser); - - const signButton = !isVierge ? ` - ${document.signatures.length > 0 && - document.signatures.filter((sig: DocumentSignature) => sig.signed).length < document.signatures.length && - canSign ? ` - - ` : ''} - ` : ''; - - 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: ${this.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 ? - `✓ Signé le ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'date inconnue'}` : - '⌛ En attente'} - -
        - `).join('')} -
        -
        -
        -
        -

        ${document.signatures.filter((sig: DocumentSignature) => sig.signed).length} out of ${document.signatures.length} signed (${(document.signatures.filter((sig: DocumentSignature) => sig.signed).length / document.signatures.length * 100).toFixed(0)}%)

        -
        - ` : ''} - ${signButton} -
        - `; - }).join('')} -
        -
        - `; - }).join('')} -
        -
        -

        Members by Role

        -
        - ${displayGroup.roles.map((role: { name: string; members: Array<{ id: string | number; name: string }> }) => ` -
        -

        ${role.name}

        -
          - ${role.members.map(member => ` -
        • ${member.name}
        • - `).join('')} -
        -
        - `).join('')} -
        -
        - `; - - - const newCloseProcessButton = document.createElement('button'); - newCloseProcessButton.className = 'close-btn'; - newCloseProcessButton.textContent = 'x'; - newCloseProcessButton.addEventListener('click', () => this.closeProcessDetails(groupId)); - - const headerButtons = detailsArea.querySelector('.header-buttons'); - if (headerButtons) { - headerButtons.appendChild(newCloseProcessButton); - } - } - } - - // Scroll down the conversation after loading messages - private scrollToBottom(container: HTMLElement) { - container.scrollTop = container.scrollHeight; - } - - - // Load the list of members - private loadMemberChat(memberId: string | number) { - this.selectedMemberId = String(memberId); - const memberMessages = this.messagesMock.find(m => String(m.memberId) === String(memberId)); - - // Find the process and the role of the member - 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); - }); - } - - - this.scrollToBottom(messagesContainer); - } - - private 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(); - this.loadMemberChat(member.id.toString()); - }; - - memberList.appendChild(memberItem); - }); - - roleElement.appendChild(memberList); - } - - - // Toggle the list of Roles - private toggleRoles(group: Group, groupElement: HTMLElement) { - console.log('=== toggleRoles START ==='); - console.log('Group:', group.name); - console.log('Group roles:', group.roles); // Afficher tous les rôles disponibles - - let roleList = groupElement.querySelector('.role-list'); - console.log('Existing roleList:', roleList); - - if (roleList) { - const roleItems = roleList.querySelectorAll('.role-item'); - roleItems.forEach(roleItem => { - console.log('Processing roleItem:', roleItem.innerHTML); // Voir le contenu HTML complet - - let container = roleItem.querySelector('.role-item-container'); - if (!container) { - container = document.createElement('div'); - container.className = 'role-item-container'; - - // Créer un span pour le nom du rôle - const nameSpan = document.createElement('span'); - nameSpan.className = 'role-name'; - nameSpan.textContent = roleItem.textContent?.trim() || ''; - - container.appendChild(nameSpan); - roleItem.textContent = ''; - roleItem.appendChild(container); - } - - // Récupérer le nom du rôle - const roleName = roleItem.textContent?.trim(); - console.log('Role name from textContent:', roleName); - - // Alternative pour obtenir le nom du rôle - const roleNameAlt = container.querySelector('.role-name')?.textContent; - console.log('Role name from span:', roleNameAlt); - - if (!container.querySelector('.folder-icon')) { - const folderButton = document.createElement('span'); - folderButton.innerHTML = '📁'; - folderButton.className = 'folder-icon'; - - folderButton.addEventListener('click', (event) => { - event.stopPropagation(); - console.log('Clicked role name:', roleName); - console.log('Available roles:', group.roles.map(r => r.name)); - - const role = group.roles.find(r => r.name === roleName); - if (role) { - console.log('Found role:', role); - this.showRoleDocuments(role, group); - } else { - console.error('Role not found. Name:', roleName); - console.error('Available roles:', group.roles); - } - }); - - container.appendChild(folderButton); - } - }); - - (roleList as HTMLElement).style.display = - (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none'; - } - } - - - private loadGroupList(): void { - const groupList = document.getElementById('group-list'); - if (!groupList) return; - - groupsMock.forEach(group => { - const li = document.createElement('li'); - li.className = 'group-list-item'; - - // Create a flex container for the name and the icon - const container = document.createElement('div'); - container.className = 'group-item-container'; - - // Span for the process name - const nameSpan = document.createElement('span'); - nameSpan.textContent = group.name; - nameSpan.className = 'process-name'; - - // Add click event to show roles - nameSpan.addEventListener('click', (event) => { - event.stopPropagation(); - this.toggleRoles(group, li); - }); - - // Add the ⚙️ icon - const settingsIcon = document.createElement('span'); - settingsIcon.textContent = '⚙️'; - settingsIcon.className = 'settings-icon'; - settingsIcon.id = `settings-${group.id}`; - - settingsIcon.onclick = (event) => { - event.stopPropagation(); - this.showProcessDetails(group, group.id); - }; - - // Assemble the elements - container.appendChild(nameSpan); - container.appendChild(settingsIcon); - li.appendChild(container); - - // Create and append the role list container - const roleList = document.createElement('ul'); - roleList.className = 'role-list'; - roleList.style.display = 'none'; - - // Add roles for this process - group.roles.forEach(role => { - const roleItem = document.createElement('li'); - roleItem.className = 'role-item'; - roleItem.textContent = role.name; - roleItem.onclick = (event) => { - event.stopPropagation(); - this.toggleMembers(role, roleItem); - }; - roleList.appendChild(roleItem); - }); - - li.appendChild(roleList); - groupList.appendChild(li); - }); - } - - - // Function to manage the list of users - private toggleUserList() { - const userList = getCorrectDOM('userList'); - if (!userList) return; - - if (!(userList as HTMLElement).classList.contains('show')) { - (userList as HTMLElement).innerHTML = membersMock.map(member => ` -
        - ${member.avatar} -
        - ${member.name} - ${member.email} -
        -
        - `).join(''); - } - (userList as HTMLElement).classList.toggle('show'); - } - - private switchUser(userId: string | number) { - const user = membersMock.find(member => member.id === userId); - if (!user) return; - currentUser = user; - this.updateCurrentUserDisplay(); - const userList = getCorrectDOM('userList') as HTMLElement; - userList?.classList.remove('show'); - } - - // Function to update the display of the current user - private updateCurrentUserDisplay() { - const userDisplay = getCorrectDOM('current-user') as HTMLElement; - if (userDisplay) { - userDisplay.innerHTML = ` - - `; - } - } - // Generate an automatic response - private 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 file - private sendFile(file: File) { - console.log('SendFile called with file:', file); - const reader = new FileReader(); - reader.onloadend = () => { - const fileData = reader.result; - const fileName = file.name; - console.log('File loaded:', fileName); - - if (this.selectedMemberId) { - messageStore.addMessage(this.selectedMemberId, { - id: Date.now(), - sender: "4NK", - fileName: fileName, - fileData: fileData, - time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), - type: 'file' - }); - console.log('Message added to store'); - - this.messagesMock = messageStore.getMessages(); - this.loadMemberChat(this.selectedMemberId); - } - }; - reader.readAsDataURL(file); - } - - // Managing the sent file - private fileList: HTMLDivElement = document.getElementById('fileList') as HTMLDivElement; - private getFileList() { - const files = Array.from(this.fileList?.querySelectorAll('.file-item') || []).map((fileItem: Element) => { - const fileName = fileItem.querySelector('.file-name')?.textContent || ''; - return { - name: fileName, - url: (fileItem as HTMLElement).dataset.content || '#', - }; - }); - return files; - } - - // New function to display the documents of a role - private 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; - files?: Array<{ name: string; url: string }>; - }>; - id?: number; - }, group: Group) { - // Load the data from localStorage - const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]'); - const storedGroup = storedGroups.find((g: Group) => g.id === group.id); - const storedRole = storedGroup?.roles.find((r: any) => r.name === role.name); - - // Use the data from localStorage if available, otherwise use the data passed as a parameter - const displayRole = storedRole || role; - - console.log('Showing documents for role:', displayRole.name, 'in group:', group.name); - // Close all existing document views first - const allDetailsAreas = document.querySelectorAll('.process-details'); - allDetailsAreas.forEach(area => { - area.remove(); - }); - - const container = document.querySelector('.container'); - if (!container) { - console.error('Container not found'); - return; - } - - // Create a new details area - const detailsArea = document.createElement('div'); - detailsArea.id = `role-documents-${displayRole.name}`; - detailsArea.className = 'process-details'; - // Filter the accessible documents - const accessibleDocuments = (displayRole.documents || []).filter((doc: { - name: string; - visibility: string; - createdAt: string | null | undefined; - deadline: string | null | undefined; - signatures: DocumentSignature[]; - id: number; - description?: string; - status?: string; - }) => - this.canUserAccessDocument(doc, displayRole.name, currentUser.processRoles?.[0]?.role || '') - ); - - detailsArea.innerHTML = ` - - `; - - container.appendChild(detailsArea); - } - - // Function to close the documents view of a role - private closeRoleDocuments(roleName: string) { - const detailsArea = document.getElementById(`role-documents-${roleName}`); - if (detailsArea) { - - detailsArea.remove(); - } - } - - private handleFiles(files: FileList, fileList: HTMLDivElement) { - Array.from(files).forEach(file => { - const reader = new FileReader(); - reader.onload = (e) => { - const fileContent = e.target?.result; - const existingFiles = fileList.querySelectorAll('.file-name'); - const isDuplicate = Array.from(existingFiles).some( - existingFile => existingFile.textContent === file.name - ); - - if (!isDuplicate) { - const fileItem = document.createElement('div'); - fileItem.className = 'file-item'; - fileItem.innerHTML = ` -
        - ${file.name} - (${(file.size / 1024).toFixed(1)} KB) -
        - - `; - fileItem.dataset.content = fileContent as string; - - const removeBtn = fileItem.querySelector('.remove-file'); - if (removeBtn) { - removeBtn.addEventListener('click', () => fileItem.remove()); - } - - fileList.appendChild(fileItem); - } - }; - reader.readAsDataURL(file); - }); - } - - // Function to manage the new request - private newRequest(params: RequestParams) { - // Add parameter validation - if (!params || !params.processId) { - console.error('Paramètres invalides:', params); - showAlert('Invalid parameters for new request'); - return; - } - - const modal = document.createElement('div'); - modal.className = 'modal-overlay'; - - // Retrieve the process with a verification - const process = groupsMock.find(g => g.id === params.processId); - if (!process) { - console.error('Processus non trouvé:', params.processId); - showAlert('Process not found'); - return; - } - - // Determine the members with an additional verification - let membersToDisplay = []; - try { - if (params.roleName === 'common') { - membersToDisplay = process.roles.reduce((members: any[], role) => { - return members.concat(role.members.map(member => ({ - ...member, - roleName: role.name - }))); - }, []); - } else { - const role = process.roles.find(r => r.name === params.roleName); - if (!role) { - throw new Error(`Role ${params.roleName} not found`); - } - membersToDisplay = role.members.map(member => ({ - ...member, - roleName: params.roleName - })); - } - } catch (error) { - console.error('Error retrieving members:', error); - showAlert('Error retrieving members'); - return; - } - - - - 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; - - // Make the area clickable - dropZone.addEventListener('click', () => { - fileInput.click(); - }); - - // Manage the file selection - fileInput.addEventListener('change', (e: Event) => { - const target = e.target as HTMLInputElement; - if (target.files && target.files.length > 0) { - this.handleFiles(target.files, fileList); - } - }); - - // Manage the 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) { - this.handleFiles(e.dataTransfer.files, fileList); - } - }); - } - - private closeModal(button: HTMLElement) { - const modalOverlay = button.closest('.modal-overlay'); - if (modalOverlay) { - modalOverlay.remove(); - } - } - - private submitNewDocument(event: Event) { - event.preventDefault(); - - const form = document.getElementById('newDocumentForm') as HTMLFormElement; - if (!form) { - showAlert('Form not found'); - return; - } - - // Retrieve the files - const fileList = document.getElementById('fileList') as HTMLDivElement; - const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map(fileItem => { - const fileName = fileItem.querySelector('.file-name')?.textContent || ''; - return { - name: fileName, - url: (fileItem as HTMLElement).dataset.content || '#', - }; - }); - - // Retrieve the values from the form - 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; - } - - try { - // Retrieve the current data - const 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; - } - - const role = group.roles.find((r: any) => - r.documents?.some((d: any) => d.id === documentId) - ); - - if (!role) { - showAlert('Role not found'); - return; - } - - // Create the new document with the signatures of the role members - 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 - })), - files: files // Ajout des fichiers au document - }; - - // Update the document in the role - const documentIndex = role.documents.findIndex((d: any) => d.id === documentId); - if (documentIndex !== -1) { - role.documents[documentIndex] = updatedDocument; - } - - // Save in localStorage - localStorage.setItem('groups', JSON.stringify(groups)); - - // Also update groupsMock for consistency - const mockGroup = groupsMock.find(g => g.id === processId); - if (mockGroup) { - const mockRole = mockGroup?.roles.find(r => r.name === role.name); - if (mockRole?.documents) { - const mockDocIndex = mockRole.documents.findIndex(d => d.id === documentId); - if (mockDocIndex !== -1) { - mockRole.documents[mockDocIndex] = { - ...updatedDocument, - status: undefined - }; - } - } - } - - // Close the modal - if (event.target instanceof HTMLElement) { - this.closeModal(event.target); - } - - // Reload the documents view with the updated data - this.showRoleDocuments(role, group); - showAlert('Document updated successfully!'); - - } catch (error) { - console.error('Error saving:', error); - showAlert('An error occurred while saving'); - } - } - - private submitCommonDocument(event: Event) { - event.preventDefault(); - - const form = document.getElementById('newDocumentForm') as HTMLFormElement; - if (!form) { - showAlert('Form not found'); - return; - } - - 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; - - if (!documentName || !description || !deadline) { - showAlert('Please fill in all required fields'); - return; - } - - try { - const 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; - } - - // Retrieve all members of all roles in the group - const allMembers = group.roles.reduce((acc: any[], role: any) => { - return acc.concat(role.members); - }, []); - - const fileList = document.getElementById('fileList') as HTMLDivElement; - const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map(fileItem => { - const fileName = fileItem.querySelector('.file-name')?.textContent || ''; - return { - name: fileName, - url: (fileItem as HTMLElement).dataset.content || '#', - }; - }); - - const updatedDocument = { - id: documentId, - name: documentName, - description: description, - createdAt: new Date().toISOString(), - deadline: deadline, - visibility: visibility, - status: "pending", - signatures: allMembers.map((member: { id: string | number; name: string }) => ({ - member: member, - signed: false, - signedAt: null - })), - files: files - }; - - // Update the common document - const documentIndex = group.commonDocuments.findIndex((d: { id: number }) => d.id === documentId); - if (documentIndex !== -1) { - group.commonDocuments[documentIndex] = updatedDocument; - } - - localStorage.setItem('groups', JSON.stringify(groups)); - - if (event.target instanceof HTMLElement) { - this.closeModal(event.target); - } - - this.showProcessDetails(group, group.id); - showAlert('Document common updated successfully!'); - - } catch (error) { - console.error('Error saving:', error); - showAlert('An error occurred while saving'); - } - } - - - private submitRequest() { - - showAlert("Request submitted!"); - } - - private closeNewRequest() { - const newRequestView = document.getElementById('new-request-view'); - if (newRequestView) { - newRequestView.style.display = 'none'; - newRequestView.remove(); - } - } - - private 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!'); - this.closeNewRequest(); - } - - // FUNCTIONS FOR SIGNATURE - - // New function to confirm the signature - private confirmSignature(documentId: number, processId: number, isCommonDocument: boolean) { - try { - // Add console.log to see the current user - console.log('Current user:', currentUser); - - const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock)); - const group = groups.find((g: Group) => g.id === processId); - - if (!group) { - throw new Error('Process not found'); - } - - let targetDoc; - if (isCommonDocument) { - targetDoc = group.commonDocuments.find((d: any) => d.id === documentId); - } else { - for (const role of group.roles) { - if (role.documents) { - targetDoc = role.documents.find((d: any) => d.id === documentId); - if (targetDoc) break; - } - } - } - - if (!targetDoc) { - throw new Error('Document not found'); - } - - const userSignature = targetDoc.signatures.find((sig: DocumentSignature) => - sig.member.name === currentUser.name - ); - - if (!userSignature) { - throw new Error(`The user ${currentUser.name} is not authorized to sign this document. Please log in with an authorized user.`); - } - - userSignature.signed = true; - userSignature.signedAt = new Date().toISOString(); - - localStorage.setItem('groups', JSON.stringify(groups)); - - const closeBtn = document.querySelector('.modal-overlay .close-btn'); - if (closeBtn instanceof HTMLElement) { - this.closeModal(closeBtn); - } - - if (isCommonDocument) { - this.showProcessDetails(group, processId); - } else { - const role = group.roles.find((r: any) => r.documents?.includes(targetDoc)); - if (role) { - this.showRoleDocuments(role, group); - } - } - - showAlert('Document signed successfully!'); - - } catch (error) { - console.error('Error signing document:', error); - showAlert(error instanceof Error ? error.message : 'Error signing document'); - } - } - - - private initializeEventListeners() { - document.addEventListener('DOMContentLoaded', (): void => { - const newRequestBtn = document.getElementById('newRequestBtn'); - if (newRequestBtn) { - newRequestBtn.addEventListener('click', (): void => { - this.newRequest({ - processId: 0, - processName: '', - roleId: 0, - roleName: '', - documentId: 0, - documentName: '' - }); - }); - } - }); - - // Gestionnaire d'événements pour le chat - const sendBtn = this.shadowRoot?.querySelector('#send-button'); - if (sendBtn) { - sendBtn.addEventListener('click', this.sendMessage.bind(this)); - } - - const messageInput = this.shadowRoot?.querySelector('#message-input'); - if (messageInput) { - messageInput.addEventListener('keypress', (event: Event) => { - if ((event as KeyboardEvent).key === 'Enter') { - event.preventDefault(); - this.sendMessage(); - } - }); - } - - // Gestionnaire pour l'envoi de fichiers - const fileInput = this.shadowRoot?.querySelector('#file-input'); - if (fileInput) { - fileInput.addEventListener('change', (event: Event) => { - const file = (event.target as HTMLInputElement).files?.[0]; - if (file) { - this.sendFile(file); - } - }); - } - } - - connectedCallback() { - if (this.shadowRoot) { - this.shadowRoot.innerHTML = ` -
        -
        -
          -
          -
          - -
          -
          - `; - } - - this.messagesMock = messageStore.getMessages(); - if (this.messagesMock.length === 0) { - messageStore.setMessages(initialMessagesMock); - this.messagesMock = messageStore.getMessages(); - } - this.updateCurrentUserDisplay(); - this.initializeEventListeners(); - this.loadGroupList(); - } -} - -customElements.define('signature-element', SignatureElement); -export { SignatureElement }; - +declare global { + interface Window { + toggleUserList: () => void; + switchUser: (userId: string | number) => void; + closeProcessDetails: (groupId: number) => void; + loadMemberChat: (memberId: string | number) => void; + closeRoleDocuments: (roleName: string) => void; + newRequest: (params: RequestParams) => void; + submitRequest: () => void; + closeNewRequest: () => void; + closeModal: (button: HTMLElement) => void; + submitDocumentRequest: (documentId: number) => void; + submitNewDocument: (event: Event) => void; + submitCommonDocument: (event: Event) => void; + signDocument: (documentId: number, processId: number, isCommonDocument: boolean) => void; + confirmSignature: (documentId: number, processId: number, isCommonDocument: boolean) => void; + } +} + +import { groupsMock } from '../../mocks/mock-signature/groupsMock'; +import { messagesMock as initialMessagesMock, messagesMock } from '../../mocks/mock-signature/messagesMock'; +import { membersMock } from '../../mocks/mock-signature/membersMocks'; +import { Message, DocumentSignature, RequestParams } from '../../models/signature.models'; +import { messageStore } from '../../utils/messageMock'; +import { showAlert } from '../account/account'; +import { Member } from '../../interface/memberInterface'; +import { Group } from '../../interface/groupInterface'; +import { getCorrectDOM } from '../../utils/document.utils'; + +let currentUser: Member = membersMock[0]; + +interface LocalNotification { + memberId: string; + text: string; + time: string; +} + +class SignatureElement extends HTMLElement { + private selectedMemberId: string | null = null; + private messagesMock: any[] = []; + private dom: Node; + private notifications: LocalNotification[] = []; + private notificationBadge = document.querySelector('.notification-badge'); + private notificationBoard = document.getElementById('notification-board'); + private notificationBell = document.getElementById('notification-bell'); + private selectedSignatories: DocumentSignature[] = []; + private allMembers = membersMock.map((member) => ({ + id: member.id, + name: member.name, + roleName: 'Default Role', + })); + + private signDocument(documentId: number, processId: number, isCommonDocument: boolean = false): void { + if (typeof window === 'undefined' || typeof document === 'undefined') { + console.error('Cette fonction ne peut être exécutée que dans un navigateur'); + return; + } + + try { + const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock)); + const group = groups.find((g: Group) => g.id === processId); + + if (!group) { + throw new Error('Process not found'); + } + + let targetDoc; + if (isCommonDocument) { + targetDoc = group.commonDocuments.find((d: any) => d.id === documentId); + } else { + for (const role of group.roles) { + if (role.documents) { + targetDoc = role.documents.find((d: any) => d.id === documentId); + if (targetDoc) break; + } + } + } + + if (!targetDoc) { + throw new Error('Document not found'); + } + + const canSign = isCommonDocument ? targetDoc.signatures?.some((sig: DocumentSignature) => sig.member?.name === currentUser?.name && !sig.signed) : this.canUserSignDocument(targetDoc, currentUser?.name, currentUser); + + if (!canSign) { + showAlert('You do not have the necessary rights to sign this document.'); + return; + } + + // Create and insert the modal directly into the body + const modalHtml = ` + `; + + document.body.insertAdjacentHTML('beforeend', modalHtml); + + // Add the slider logic after creating the modal + const slider = document.getElementById('signatureSlider'); + const sliderTrack = slider?.parentElement; + let isDragging = false; + let startX: number; + let sliderLeft: number; + + if (slider && sliderTrack) { + slider.addEventListener('mousedown', initDrag.bind(this)); + document.addEventListener('mousemove', drag.bind(this)); + document.addEventListener('mouseup', stopDrag.bind(this)); + + // Touch events for mobile + slider.addEventListener('touchstart', initDrag.bind(this)); + document.addEventListener('touchmove', drag.bind(this)); + document.addEventListener('touchend', stopDrag.bind(this)); + } + + function initDrag(e: MouseEvent | TouchEvent) { + isDragging = true; + startX = 'touches' in e ? e.touches[0].clientX : e.clientX; + sliderLeft = slider?.offsetLeft || 0; + } + + function drag(this: SignatureElement, e: MouseEvent | TouchEvent) { + if (!isDragging || !slider || !sliderTrack) return; + + e.preventDefault(); + const rect = sliderTrack.getBoundingClientRect(); + const x = 'touches' in e ? e.touches[0].clientX : e.clientX; + + // Calculate the position relative to the track + let newLeft = x - rect.left - slider.offsetWidth / 2; + + // Limit the movement + const maxLeft = sliderTrack.offsetWidth - slider.offsetWidth; + newLeft = Math.max(0, Math.min(newLeft, maxLeft)); + + // Update the position + slider.style.left = `${newLeft}px`; + + // If the slider reaches 90% of the path, trigger the signature + if (newLeft > maxLeft * 0.9) { + stopDrag(e); + this.confirmSignature(documentId, processId, isCommonDocument); + } + } + + function stopDrag(e: MouseEvent | TouchEvent) { + if (!isDragging || !slider) return; + isDragging = false; + + // Reset the position if not enough dragged + if (slider.offsetLeft < (sliderTrack?.offsetWidth || 0) * 0.9) { + slider.style.left = '0px'; + } + } + } catch (error) { + console.error('Error displaying modal:', error); + showAlert(error instanceof Error ? error.message : 'Error displaying modal'); + } + } + + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this.dom = getCorrectDOM('signature-element'); + + window.toggleUserList = this.toggleUserList.bind(this); + window.switchUser = this.switchUser.bind(this); + window.closeProcessDetails = this.closeProcessDetails.bind(this); + window.loadMemberChat = this.loadMemberChat.bind(this); + window.closeRoleDocuments = this.closeRoleDocuments.bind(this); + window.newRequest = this.newRequest.bind(this); + window.submitRequest = this.submitRequest.bind(this); + window.closeNewRequest = this.closeNewRequest.bind(this); + window.closeModal = this.closeModal.bind(this); + window.submitNewDocument = this.submitNewDocument.bind(this); + window.submitCommonDocument = this.submitCommonDocument.bind(this); + window.signDocument = this.signDocument.bind(this); + window.confirmSignature = this.confirmSignature.bind(this); + window.submitDocumentRequest = this.submitDocumentRequest.bind(this); + + // Initialiser les événements de notification + document.addEventListener('click', (event: Event): void => { + if (this.notificationBoard && this.notificationBoard.style.display === 'block' && !this.notificationBoard.contains(event.target as Node) && this.notificationBell && !this.notificationBell.contains(event.target as Node)) { + this.notificationBoard.style.display = 'none'; + } + }); + this.initMessageEvents(); + this.initFileUpload(); + } + + private initMessageEvents() { + // Pour le bouton Send + const sendButton = document.getElementById('send-button'); + if (sendButton) { + sendButton.addEventListener('click', () => this.sendMessage()); + } + + // Pour la touche Entrée + const messageInput = document.getElementById('message-input'); + if (messageInput) { + messageInput.addEventListener('keypress', (event: KeyboardEvent) => { + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); + this.sendMessage(); + } + }); + } + } + + private initFileUpload() { + const fileInput = document.getElementById('file-input') as HTMLInputElement; + if (fileInput) { + fileInput.addEventListener('change', (event: Event) => { + const target = event.target as HTMLInputElement; + if (target.files && target.files.length > 0) { + this.sendFile(target.files[0]); + } + }); + } + } + + private 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)); + } + + // Add this helper function + private canUserAccessDocument(document: any, roleId: string, currentUserRole: string): boolean { + // Modify the access logic + if (document.visibility === 'public') { + return true; // Can see but not necessarily sign + } + return roleId === currentUserRole; + } + + private canUserSignDocument(document: any, role: string, user: Member): boolean { + console.log('Checking signing rights for:', { + document, + role, + user, + userRoles: user.processRoles, + }); + + // Vérifier si l'utilisateur est dans la liste des signatures + const isSignatory = document.signatures?.some((sig: DocumentSignature) => sig.member && 'id' in sig.member && sig.member.id === user.id && !sig.signed); + + if (!isSignatory) { + console.log('User is not in signatures list or has already signed'); + return false; + } + + // Si l'utilisateur est dans la liste des signatures, il peut signer + return true; + } + + private 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'; + } + } + + ///////////////////// Notification module ///////////////////// + // Delete a notification + private removeNotification(index: number) { + this.notifications?.splice(index, 1); // Ajout de ?. + this.renderNotifications(); + this.updateNotificationBadge(); + } + // Show notifications + private renderNotifications() { + if (!this.notificationBoard) return; + + // Reset the interface + this.notificationBoard.innerHTML = ''; + + // Displays "No notifications available" if there are no notifications + if (this.notifications.length === 0) { + this.notificationBoard.innerHTML = '
          No notifications available
          '; + return; + } + + // Add each notification to the list + this.notifications.forEach((notif, index) => { + const notifElement = document.createElement('div'); + notifElement.className = 'notification-item'; + notifElement.textContent = `${notif.text} at ${notif.time}`; + notifElement.onclick = () => { + this.loadMemberChat(notif.memberId); + this.removeNotification(index); + }; + this.notificationBoard?.appendChild(notifElement); + }); + } + private updateNotificationBadge() { + if (!this.notificationBadge) return; + const count = this.notifications.length; + this.notificationBadge.textContent = count > 99 ? '+99' : count.toString(); + (this.notificationBadge as HTMLElement).style.display = count > 0 ? 'block' : 'none'; + } + + // Add notification + private 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 + this.notifications.push(notification); + this.renderNotifications(); + this.updateNotificationBadge(); + } + // Send a messsage + private sendMessage() { + const messageInput = document.getElementById('message-input') as HTMLInputElement; + if (!messageInput) return; + const messageText = messageInput.value.trim(); + + if (messageText === '' || this.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, + }; + // Add and display the message immediately + messageStore.addMessage(this.selectedMemberId, newMessage); + this.messagesMock = messageStore.getMessages(); + this.loadMemberChat(this.selectedMemberId); + + // Reset the input + messageInput.value = ''; + + // Automatic response after 2 seconds + setTimeout(() => { + if (this.selectedMemberId) { + const autoReply = this.generateAutoReply(`Member ${this.selectedMemberId}`); + messageStore.addMessage(this.selectedMemberId, autoReply); + this.messagesMock = messageStore.getMessages(); + this.loadMemberChat(this.selectedMemberId); + this.addNotification(this.selectedMemberId, autoReply); + } + }, 2000); + } + + private showProcessDetails(group: Group, groupId: number) { + console.log('Showing details for group:', groupId); + + // Close all existing process views + 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; + } + + // Load the data from localStorage + const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]'); + const storedGroup = storedGroups.find((g: Group) => g.id === groupId); + + // Use the data from localStorage if available, otherwise use the group passed as a parameter + const displayGroup = storedGroup || group; + + 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 = ` +
          +

          ${displayGroup.name}

          +
          +
          +
          +
          +
          +

          Description

          +

          ${displayGroup.description || 'No description available'}

          +
          +
          +

          Documents Communs

          +
          + ${displayGroup.commonDocuments + .map((document: any) => { + const totalSignatures = document.signatures?.length || 0; + const signedCount = document.signatures?.filter((sig: DocumentSignature) => sig.signed).length || 0; + const percentage = totalSignatures > 0 ? (signedCount / totalSignatures) * 100 : 0; + const isVierge = !document.createdAt || !document.deadline || !document.signatures?.length; + const canSign = document.signatures?.some((sig: DocumentSignature) => sig.member && 'id' in sig.member && sig.member.id === currentUser.id && !sig.signed); + + const signButton = !isVierge + ? ` + ${ + totalSignatures > 0 && signedCount < totalSignatures && canSign + ? ` + + ` + : '' + } + ` + : ''; + + 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: ${this.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 - Waiting for creation

          + + ` + } + ${signButton} +
          +
          + `; + }) + .join('')} +
          +
          +
          +

          Roles and Documents

          + ${displayGroup.roles + .map((role: { name: string; documents?: any[] }) => { + // Filter the documents according to the access rights + const accessibleDocuments = (role.documents || []).filter((doc) => this.canUserAccessDocument(doc, role.name, currentUser.processRoles?.[0]?.role || '')); + + return ` +
          +

          ${role.name}

          +
          + ${accessibleDocuments + .map((document) => { + const isVierge = !document.createdAt || !document.deadline || document.signatures.length === 0; + + const canSign = this.canUserSignDocument(document, role.name, currentUser); + + const signButton = !isVierge + ? ` + ${ + document.signatures.length > 0 && document.signatures.filter((sig: DocumentSignature) => sig.signed).length < document.signatures.length && canSign + ? ` + + ` + : '' + } + ` + : ''; + + 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: ${this.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 ? `✓ Signé le ${sig.signedAt ? new Date(sig.signedAt).toLocaleDateString() : 'date inconnue'}` : '⌛ En attente'} + +
          + `, + ) + .join('')} +
          +
          +
          +
          +

          ${document.signatures.filter((sig: DocumentSignature) => sig.signed).length} out of ${document.signatures.length} signed (${((document.signatures.filter((sig: DocumentSignature) => sig.signed).length / document.signatures.length) * 100).toFixed(0)}%)

          +
          + ` + : '' + } + ${signButton} +
          + `; + }) + .join('')} +
          +
          + `; + }) + .join('')} +
          +
          +

          Members by Role

          +
          + ${displayGroup.roles + .map( + (role: { name: string; members: Array<{ id: string | number; name: string }> }) => ` +
          +

          ${role.name}

          +
            + ${role.members + .map( + (member) => ` +
          • ${member.name}
          • + `, + ) + .join('')} +
          +
          + `, + ) + .join('')} +
          +
          + `; + + const newCloseProcessButton = document.createElement('button'); + newCloseProcessButton.className = 'close-btn'; + newCloseProcessButton.textContent = 'x'; + newCloseProcessButton.addEventListener('click', () => this.closeProcessDetails(groupId)); + + const headerButtons = detailsArea.querySelector('.header-buttons'); + if (headerButtons) { + headerButtons.appendChild(newCloseProcessButton); + } + } + } + + // Scroll down the conversation after loading messages + private scrollToBottom(container: HTMLElement) { + container.scrollTop = container.scrollHeight; + } + + // Load the list of members + private loadMemberChat(memberId: string | number) { + this.selectedMemberId = String(memberId); + const memberMessages = this.messagesMock.find((m) => String(m.memberId) === String(memberId)); + + // Find the process and the role of the member + 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); + }); + } + + this.scrollToBottom(messagesContainer); + } + + private 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(); + this.loadMemberChat(member.id.toString()); + }; + + memberList.appendChild(memberItem); + }); + + roleElement.appendChild(memberList); + } + + // Toggle the list of Roles + private toggleRoles(group: Group, groupElement: HTMLElement) { + console.log('=== toggleRoles START ==='); + console.log('Group:', group.name); + console.log('Group roles:', group.roles); // Afficher tous les rôles disponibles + + let roleList = groupElement.querySelector('.role-list'); + console.log('Existing roleList:', roleList); + + if (roleList) { + const roleItems = roleList.querySelectorAll('.role-item'); + roleItems.forEach((roleItem) => { + console.log('Processing roleItem:', roleItem.innerHTML); // Voir le contenu HTML complet + + let container = roleItem.querySelector('.role-item-container'); + if (!container) { + container = document.createElement('div'); + container.className = 'role-item-container'; + + // Créer un span pour le nom du rôle + const nameSpan = document.createElement('span'); + nameSpan.className = 'role-name'; + nameSpan.textContent = roleItem.textContent?.trim() || ''; + + container.appendChild(nameSpan); + roleItem.textContent = ''; + roleItem.appendChild(container); + } + + // Récupérer le nom du rôle + const roleName = roleItem.textContent?.trim(); + console.log('Role name from textContent:', roleName); + + // Alternative pour obtenir le nom du rôle + const roleNameAlt = container.querySelector('.role-name')?.textContent; + console.log('Role name from span:', roleNameAlt); + + if (!container.querySelector('.folder-icon')) { + const folderButton = document.createElement('span'); + folderButton.innerHTML = '📁'; + folderButton.className = 'folder-icon'; + + folderButton.addEventListener('click', (event) => { + event.stopPropagation(); + console.log('Clicked role name:', roleName); + console.log( + 'Available roles:', + group.roles.map((r) => r.name), + ); + + const role = group.roles.find((r) => r.name === roleName); + if (role) { + console.log('Found role:', role); + this.showRoleDocuments(role, group); + } else { + console.error('Role not found. Name:', roleName); + console.error('Available roles:', group.roles); + } + }); + + container.appendChild(folderButton); + } + }); + + (roleList as HTMLElement).style.display = (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none'; + } + } + + private loadGroupList(): void { + const groupList = document.getElementById('group-list'); + if (!groupList) return; + + groupsMock.forEach((group) => { + const li = document.createElement('li'); + li.className = 'group-list-item'; + + // Create a flex container for the name and the icon + const container = document.createElement('div'); + container.className = 'group-item-container'; + + // Span for the process name + const nameSpan = document.createElement('span'); + nameSpan.textContent = group.name; + nameSpan.className = 'process-name'; + + // Add click event to show roles + nameSpan.addEventListener('click', (event) => { + event.stopPropagation(); + this.toggleRoles(group, li); + }); + + // Add the ⚙️ icon + const settingsIcon = document.createElement('span'); + settingsIcon.textContent = '⚙️'; + settingsIcon.className = 'settings-icon'; + settingsIcon.id = `settings-${group.id}`; + + settingsIcon.onclick = (event) => { + event.stopPropagation(); + this.showProcessDetails(group, group.id); + }; + + // Assemble the elements + container.appendChild(nameSpan); + container.appendChild(settingsIcon); + li.appendChild(container); + + // Create and append the role list container + const roleList = document.createElement('ul'); + roleList.className = 'role-list'; + roleList.style.display = 'none'; + + // Add roles for this process + group.roles.forEach((role) => { + const roleItem = document.createElement('li'); + roleItem.className = 'role-item'; + roleItem.textContent = role.name; + roleItem.onclick = (event) => { + event.stopPropagation(); + this.toggleMembers(role, roleItem); + }; + roleList.appendChild(roleItem); + }); + + li.appendChild(roleList); + groupList.appendChild(li); + }); + } + + // Function to manage the list of users + private toggleUserList() { + const userList = getCorrectDOM('userList'); + if (!userList) return; + + if (!(userList as HTMLElement).classList.contains('show')) { + (userList as HTMLElement).innerHTML = membersMock + .map( + (member) => ` +
          + ${member.avatar} +
          + ${member.name} + ${member.email} +
          +
          + `, + ) + .join(''); + } + (userList as HTMLElement).classList.toggle('show'); + } + + private switchUser(userId: string | number) { + const user = membersMock.find((member) => member.id === userId); + if (!user) return; + currentUser = user; + this.updateCurrentUserDisplay(); + const userList = getCorrectDOM('userList') as HTMLElement; + userList?.classList.remove('show'); + } + + // Function to update the display of the current user + private updateCurrentUserDisplay() { + const userDisplay = getCorrectDOM('current-user') as HTMLElement; + if (userDisplay) { + userDisplay.innerHTML = ` + + `; + } + } + // Generate an automatic response + private 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 file + private sendFile(file: File) { + console.log('SendFile called with file:', file); + const reader = new FileReader(); + reader.onloadend = () => { + const fileData = reader.result; + const fileName = file.name; + console.log('File loaded:', fileName); + + if (this.selectedMemberId) { + messageStore.addMessage(this.selectedMemberId, { + id: Date.now(), + sender: '4NK', + fileName: fileName, + fileData: fileData, + time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + type: 'file', + }); + console.log('Message added to store'); + + this.messagesMock = messageStore.getMessages(); + this.loadMemberChat(this.selectedMemberId); + } + }; + reader.readAsDataURL(file); + } + + // Managing the sent file + private fileList: HTMLDivElement = document.getElementById('fileList') as HTMLDivElement; + private getFileList() { + const files = Array.from(this.fileList?.querySelectorAll('.file-item') || []).map((fileItem: Element) => { + const fileName = fileItem.querySelector('.file-name')?.textContent || ''; + return { + name: fileName, + url: (fileItem as HTMLElement).dataset.content || '#', + }; + }); + return files; + } + + // New function to display the documents of a role + private 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; + files?: Array<{ name: string; url: string }>; + }>; + id?: number; + }, + group: Group, + ) { + // Load the data from localStorage + const storedGroups = JSON.parse(localStorage.getItem('groups') || '[]'); + const storedGroup = storedGroups.find((g: Group) => g.id === group.id); + const storedRole = storedGroup?.roles.find((r: any) => r.name === role.name); + + // Use the data from localStorage if available, otherwise use the data passed as a parameter + const displayRole = storedRole || role; + + console.log('Showing documents for role:', displayRole.name, 'in group:', group.name); + // Close all existing document views first + const allDetailsAreas = document.querySelectorAll('.process-details'); + allDetailsAreas.forEach((area) => { + area.remove(); + }); + + const container = document.querySelector('.container'); + if (!container) { + console.error('Container not found'); + return; + } + + // Create a new details area + const detailsArea = document.createElement('div'); + detailsArea.id = `role-documents-${displayRole.name}`; + detailsArea.className = 'process-details'; + // Filter the accessible documents + const accessibleDocuments = (displayRole.documents || []).filter((doc: { name: string; visibility: string; createdAt: string | null | undefined; deadline: string | null | undefined; signatures: DocumentSignature[]; id: number; description?: string; status?: string }) => + this.canUserAccessDocument(doc, displayRole.name, currentUser.processRoles?.[0]?.role || ''), + ); + + detailsArea.innerHTML = ` + + `; + + container.appendChild(detailsArea); + } + + // Function to close the documents view of a role + private closeRoleDocuments(roleName: string) { + const detailsArea = document.getElementById(`role-documents-${roleName}`); + if (detailsArea) { + detailsArea.remove(); + } + } + + private handleFiles(files: FileList, fileList: HTMLDivElement) { + Array.from(files).forEach((file) => { + const reader = new FileReader(); + reader.onload = (e) => { + const fileContent = e.target?.result; + const existingFiles = fileList.querySelectorAll('.file-name'); + const isDuplicate = Array.from(existingFiles).some((existingFile) => existingFile.textContent === file.name); + + if (!isDuplicate) { + const fileItem = document.createElement('div'); + fileItem.className = 'file-item'; + fileItem.innerHTML = ` +
          + ${file.name} + (${(file.size / 1024).toFixed(1)} KB) +
          + + `; + fileItem.dataset.content = fileContent as string; + + const removeBtn = fileItem.querySelector('.remove-file'); + if (removeBtn) { + removeBtn.addEventListener('click', () => fileItem.remove()); + } + + fileList.appendChild(fileItem); + } + }; + reader.readAsDataURL(file); + }); + } + + // Function to manage the new request + private newRequest(params: RequestParams) { + // Add parameter validation + if (!params || !params.processId) { + console.error('Paramètres invalides:', params); + showAlert('Invalid parameters for new request'); + return; + } + + const modal = document.createElement('div'); + modal.className = 'modal-overlay'; + + // Retrieve the process with a verification + const process = groupsMock.find((g) => g.id === params.processId); + if (!process) { + console.error('Processus non trouvé:', params.processId); + showAlert('Process not found'); + return; + } + + // Determine the members with an additional verification + let membersToDisplay = []; + try { + if (params.roleName === 'common') { + membersToDisplay = process.roles.reduce((members: any[], role) => { + return members.concat( + role.members.map((member) => ({ + ...member, + roleName: role.name, + })), + ); + }, []); + } else { + const role = process.roles.find((r) => r.name === params.roleName); + if (!role) { + throw new Error(`Role ${params.roleName} not found`); + } + membersToDisplay = role.members.map((member) => ({ + ...member, + roleName: params.roleName, + })); + } + } catch (error) { + console.error('Error retrieving members:', error); + showAlert('Error retrieving members'); + return; + } + + 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; + + // Make the area clickable + dropZone.addEventListener('click', () => { + fileInput.click(); + }); + + // Manage the file selection + fileInput.addEventListener('change', (e: Event) => { + const target = e.target as HTMLInputElement; + if (target.files && target.files.length > 0) { + this.handleFiles(target.files, fileList); + } + }); + + // Manage the 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) { + this.handleFiles(e.dataTransfer.files, fileList); + } + }); + } + + private closeModal(button: HTMLElement) { + const modalOverlay = button.closest('.modal-overlay'); + if (modalOverlay) { + modalOverlay.remove(); + } + } + + private submitNewDocument(event: Event) { + event.preventDefault(); + + const form = document.getElementById('newDocumentForm') as HTMLFormElement; + if (!form) { + showAlert('Form not found'); + return; + } + + // Retrieve the files + const fileList = document.getElementById('fileList') as HTMLDivElement; + const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map((fileItem) => { + const fileName = fileItem.querySelector('.file-name')?.textContent || ''; + return { + name: fileName, + url: (fileItem as HTMLElement).dataset.content || '#', + }; + }); + + // Retrieve the values from the form + 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; + } + + try { + // Retrieve the current data + const 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; + } + + const role = group.roles.find((r: any) => r.documents?.some((d: any) => d.id === documentId)); + + if (!role) { + showAlert('Role not found'); + return; + } + + // Create the new document with the signatures of the role members + 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, + })), + files: files, // Ajout des fichiers au document + }; + + // Update the document in the role + const documentIndex = role.documents.findIndex((d: any) => d.id === documentId); + if (documentIndex !== -1) { + role.documents[documentIndex] = updatedDocument; + } + + // Save in localStorage + localStorage.setItem('groups', JSON.stringify(groups)); + + // Also update groupsMock for consistency + const mockGroup = groupsMock.find((g) => g.id === processId); + if (mockGroup) { + const mockRole = mockGroup?.roles.find((r) => r.name === role.name); + if (mockRole?.documents) { + const mockDocIndex = mockRole.documents.findIndex((d) => d.id === documentId); + if (mockDocIndex !== -1) { + mockRole.documents[mockDocIndex] = { + ...updatedDocument, + status: undefined, + }; + } + } + } + + // Close the modal + if (event.target instanceof HTMLElement) { + this.closeModal(event.target); + } + + // Reload the documents view with the updated data + this.showRoleDocuments(role, group); + showAlert('Document updated successfully!'); + } catch (error) { + console.error('Error saving:', error); + showAlert('An error occurred while saving'); + } + } + + private submitCommonDocument(event: Event) { + event.preventDefault(); + + const form = document.getElementById('newDocumentForm') as HTMLFormElement; + if (!form) { + showAlert('Form not found'); + return; + } + + 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; + + if (!documentName || !description || !deadline) { + showAlert('Please fill in all required fields'); + return; + } + + try { + const 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; + } + + // Retrieve all members of all roles in the group + const allMembers = group.roles.reduce((acc: any[], role: any) => { + return acc.concat(role.members); + }, []); + + const fileList = document.getElementById('fileList') as HTMLDivElement; + const files = Array.from(fileList?.querySelectorAll('.file-item') || []).map((fileItem) => { + const fileName = fileItem.querySelector('.file-name')?.textContent || ''; + return { + name: fileName, + url: (fileItem as HTMLElement).dataset.content || '#', + }; + }); + + const updatedDocument = { + id: documentId, + name: documentName, + description: description, + createdAt: new Date().toISOString(), + deadline: deadline, + visibility: visibility, + status: 'pending', + signatures: allMembers.map((member: { id: string | number; name: string }) => ({ + member: member, + signed: false, + signedAt: null, + })), + files: files, + }; + + // Update the common document + const documentIndex = group.commonDocuments.findIndex((d: { id: number }) => d.id === documentId); + if (documentIndex !== -1) { + group.commonDocuments[documentIndex] = updatedDocument; + } + + localStorage.setItem('groups', JSON.stringify(groups)); + + if (event.target instanceof HTMLElement) { + this.closeModal(event.target); + } + + this.showProcessDetails(group, group.id); + showAlert('Document common updated successfully!'); + } catch (error) { + console.error('Error saving:', error); + showAlert('An error occurred while saving'); + } + } + + private submitRequest() { + showAlert('Request submitted!'); + } + + private closeNewRequest() { + const newRequestView = document.getElementById('new-request-view'); + if (newRequestView) { + newRequestView.style.display = 'none'; + newRequestView.remove(); + } + } + + private 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!'); + this.closeNewRequest(); + } + + // FUNCTIONS FOR SIGNATURE + + // New function to confirm the signature + private confirmSignature(documentId: number, processId: number, isCommonDocument: boolean) { + try { + // Add console.log to see the current user + console.log('Current user:', currentUser); + + const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock)); + const group = groups.find((g: Group) => g.id === processId); + + if (!group) { + throw new Error('Process not found'); + } + + let targetDoc; + if (isCommonDocument) { + targetDoc = group.commonDocuments.find((d: any) => d.id === documentId); + } else { + for (const role of group.roles) { + if (role.documents) { + targetDoc = role.documents.find((d: any) => d.id === documentId); + if (targetDoc) break; + } + } + } + + if (!targetDoc) { + throw new Error('Document not found'); + } + + const userSignature = targetDoc.signatures.find((sig: DocumentSignature) => sig.member.name === currentUser.name); + + if (!userSignature) { + throw new Error(`The user ${currentUser.name} is not authorized to sign this document. Please log in with an authorized user.`); + } + + userSignature.signed = true; + userSignature.signedAt = new Date().toISOString(); + + localStorage.setItem('groups', JSON.stringify(groups)); + + const closeBtn = document.querySelector('.modal-overlay .close-btn'); + if (closeBtn instanceof HTMLElement) { + this.closeModal(closeBtn); + } + + if (isCommonDocument) { + this.showProcessDetails(group, processId); + } else { + const role = group.roles.find((r: any) => r.documents?.includes(targetDoc)); + if (role) { + this.showRoleDocuments(role, group); + } + } + + showAlert('Document signed successfully!'); + } catch (error) { + console.error('Error signing document:', error); + showAlert(error instanceof Error ? error.message : 'Error signing document'); + } + } + + private initializeEventListeners() { + document.addEventListener('DOMContentLoaded', (): void => { + const newRequestBtn = document.getElementById('newRequestBtn'); + if (newRequestBtn) { + newRequestBtn.addEventListener('click', (): void => { + this.newRequest({ + processId: 0, + processName: '', + roleId: 0, + roleName: '', + documentId: 0, + documentName: '', + }); + }); + } + }); + + // Gestionnaire d'événements pour le chat + const sendBtn = this.shadowRoot?.querySelector('#send-button'); + if (sendBtn) { + sendBtn.addEventListener('click', this.sendMessage.bind(this)); + } + + const messageInput = this.shadowRoot?.querySelector('#message-input'); + if (messageInput) { + messageInput.addEventListener('keypress', (event: Event) => { + if ((event as KeyboardEvent).key === 'Enter') { + event.preventDefault(); + this.sendMessage(); + } + }); + } + + // Gestionnaire pour l'envoi de fichiers + const fileInput = this.shadowRoot?.querySelector('#file-input'); + if (fileInput) { + fileInput.addEventListener('change', (event: Event) => { + const file = (event.target as HTMLInputElement).files?.[0]; + if (file) { + this.sendFile(file); + } + }); + } + } + + connectedCallback() { + if (this.shadowRoot) { + this.shadowRoot.innerHTML = ` +
          +
          +
            +
            +
            + +
            +
            + `; + } + + this.messagesMock = messageStore.getMessages(); + if (this.messagesMock.length === 0) { + messageStore.setMessages(initialMessagesMock); + this.messagesMock = messageStore.getMessages(); + } + this.updateCurrentUserDisplay(); + this.initializeEventListeners(); + this.loadGroupList(); + } +} + +customElements.define('signature-element', SignatureElement); +export { SignatureElement }; diff --git a/src/router.ts b/src/router.ts index 2e51714..baa8682 100755 --- a/src/router.ts +++ b/src/router.ts @@ -6,7 +6,7 @@ import { cleanSubscriptions } from './utils/subscription.utils'; import { LoginComponent } from './pages/home/home-component'; import { prepareAndSendPairingTx } from './utils/sp-address.utils'; import ModalService from './services/modal.service'; -export {Services}; +export { Services }; const routes: { [key: string]: string } = { home: '/src/pages/home/home.html', process: '/src/pages/process/process.html', @@ -20,7 +20,7 @@ export let currentRoute = ''; export async function navigate(path: string) { cleanSubscriptions(); - cleanPage() + cleanPage(); path = path.replace(/^\//, ''); if (path.includes('/')) { const parsedPath = path.split('/')[0]; @@ -42,81 +42,80 @@ async function handleLocation(path: string) { const content = document.getElementById('containerId'); if (content) { - - if(path === 'home' ) { - const login = LoginComponent + if (path === 'home') { + const login = LoginComponent; const container = document.querySelector('#containerId'); const accountComponent = document.createElement('login-4nk-component'); - accountComponent.setAttribute('style', 'width: 100vw; height: 100vh; position: relative; grid-row: 2;') - if(container) container.appendChild(accountComponent) - } else if(path !== 'process') { + accountComponent.setAttribute('style', 'width: 100vw; height: 100vh; position: relative; grid-row: 2;'); + if (container) container.appendChild(accountComponent); + } else if (path !== 'process') { const html = await fetch(routeHtml).then((data) => data.text()); content.innerHTML = html; - } + } - await new Promise(requestAnimationFrame); - injectHeader(); + await new Promise(requestAnimationFrame); + injectHeader(); - // const modalService = await ModalService.getInstance() - // modalService.injectValidationModal() - switch (path) { - case 'process': - // const { init } = await import('./pages/process/process'); - const { ProcessListComponent } = await import('./pages/process/process-list-component'); + // const modalService = await ModalService.getInstance() + // modalService.injectValidationModal() + switch (path) { + case 'process': + // const { init } = await import('./pages/process/process'); + const { ProcessListComponent } = await import('./pages/process/process-list-component'); - const container2 = document.querySelector('#containerId'); - const accountComponent = document.createElement('process-list-4nk-component'); - - if (!customElements.get('process-list-4nk-component')) { - customElements.define('process-list-4nk-component', ProcessListComponent); - } - accountComponent.setAttribute('style', 'height: 100vh; position: relative; grid-row: 2; grid-column: 4;') - if(container2) container2.appendChild(accountComponent) - break; + const container2 = document.querySelector('#containerId'); + const accountComponent = document.createElement('process-list-4nk-component'); - case 'process-element': - if (parsedPath && parsedPath.length) { - const { initProcessElement } = await import('./pages/process-element/process-element'); - const parseProcess = parsedPath[1].split('_'); - initProcessElement(parseProcess[0], parseProcess[1]); - } - break; - - case 'account': - const { AccountComponent } = await import('./pages/account/account-component'); - const accountContainer = document.querySelector('.parameter-list'); - if (accountContainer) { - if (!customElements.get('account-component')) { - customElements.define('account-component', AccountComponent); + if (!customElements.get('process-list-4nk-component')) { + customElements.define('process-list-4nk-component', ProcessListComponent); } - const accountComponent = document.createElement('account-component'); - accountContainer.appendChild(accountComponent); - } - break; + accountComponent.setAttribute('style', 'height: 100vh; position: relative; grid-row: 2; grid-column: 4;'); + if (container2) container2.appendChild(accountComponent); + break; - case 'chat': - const { ChatComponent } = await import('./pages/chat/chat-component'); - const chatContainer = document.querySelector('.group-list'); - if (chatContainer) { - if (!customElements.get('chat-component')) { - customElements.define('chat-component', ChatComponent); + case 'process-element': + if (parsedPath && parsedPath.length) { + const { initProcessElement } = await import('./pages/process-element/process-element'); + const parseProcess = parsedPath[1].split('_'); + initProcessElement(parseProcess[0], parseProcess[1]); } - const chatComponent = document.createElement('chat-component'); - chatContainer.appendChild(chatComponent); - } - break; + break; - case 'signature': - const { SignatureComponent } = await import('./pages/signature/signature-component'); - const container = document.querySelector('.group-list'); - if (container) { - if (!customElements.get('signature-component')) { - customElements.define('signature-component', SignatureComponent); + case 'account': + const { AccountComponent } = await import('./pages/account/account-component'); + const accountContainer = document.querySelector('.parameter-list'); + if (accountContainer) { + if (!customElements.get('account-component')) { + customElements.define('account-component', AccountComponent); + } + const accountComponent = document.createElement('account-component'); + accountContainer.appendChild(accountComponent); } - const signatureComponent = document.createElement('signature-component'); - container.appendChild(signatureComponent); - } - break; + break; + + case 'chat': + const { ChatComponent } = await import('./pages/chat/chat-component'); + const chatContainer = document.querySelector('.group-list'); + if (chatContainer) { + if (!customElements.get('chat-component')) { + customElements.define('chat-component', ChatComponent); + } + const chatComponent = document.createElement('chat-component'); + chatContainer.appendChild(chatComponent); + } + break; + + case 'signature': + const { SignatureComponent } = await import('./pages/signature/signature-component'); + const container = document.querySelector('.group-list'); + if (container) { + if (!customElements.get('signature-component')) { + customElements.define('signature-component', SignatureComponent); + } + const signatureComponent = document.createElement('signature-component'); + container.appendChild(signatureComponent); + } + break; } } } @@ -157,7 +156,7 @@ export async function init(): Promise { setTimeout(async () => { try { // check if we have a shared secret with that address - await prepareAndSendPairingTx(pairingAddress) + await prepareAndSendPairingTx(pairingAddress); } catch (e) { console.error('Failed to pair:', e); } @@ -174,7 +173,7 @@ export async function init(): Promise { async function cleanPage() { const container = document.querySelector('#containerId'); - if(container) container.innerHTML = '' + if (container) container.innerHTML = ''; } async function injectHeader() { diff --git a/src/service-workers/cache.worker.js b/src/service-workers/cache.worker.js index 42e720c..966eb99 100644 --- a/src/service-workers/cache.worker.js +++ b/src/service-workers/cache.worker.js @@ -1,20 +1,8 @@ -const addResourcesToCache = async (resources) => { - const cache = await caches.open("v1"); - await cache.addAll(resources); - }; - - self.addEventListener("install", (event) => { - event.waitUntil( - addResourcesToCache([ - "/", - "/index.html", - "/style.css", - "/app.js", - "/image-list.js", - "/star-wars-logo.jpg", - "/gallery/bountyHunters.jpg", - "/gallery/myLittleVader.jpg", - "/gallery/snowTroopers.jpg", - ]), - ); - }); \ No newline at end of file +const addResourcesToCache = async (resources) => { + const cache = await caches.open('v1'); + await cache.addAll(resources); +}; + +self.addEventListener('install', (event) => { + event.waitUntil(addResourcesToCache(['/', '/index.html', '/style.css', '/app.js', '/image-list.js', '/star-wars-logo.jpg', '/gallery/bountyHunters.jpg', '/gallery/myLittleVader.jpg', '/gallery/snowTroopers.jpg'])); +}); diff --git a/src/service-workers/database.worker.js b/src/service-workers/database.worker.js index 8e160ed..722a990 100755 --- a/src/service-workers/database.worker.js +++ b/src/service-workers/database.worker.js @@ -9,21 +9,21 @@ self.addEventListener('activate', (event) => { // Event listener for messages from clients self.addEventListener('message', async (event) => { const data = event.data; - if(data.type === 'START') { + if (data.type === 'START') { const fetchNotifications = async () => { const itemsWithFlag = await getAllItemsWithFlag(); - + // Process items with the specific flag - itemsWithFlag?.forEach(item => { - console.log(item); // Do something with each flagged item + itemsWithFlag?.forEach((item) => { + console.log(item); // Do something with each flagged item }); - - event.ports[0].postMessage({ - type: "NOTIFICATIONS", - data: itemsWithFlag - }); - } - fetchNotifications() + + event.ports[0].postMessage({ + type: 'NOTIFICATIONS', + data: itemsWithFlag, + }); + }; + fetchNotifications(); setInterval(fetchNotifications, 2 * 60 * 1000); } @@ -67,7 +67,7 @@ async function openDatabase() { async function getAllItemsWithFlag() { const db = await openDatabase(); - + // Function to get all processes because it is asynchronous const getAllProcesses = () => { return new Promise((resolve, reject) => { @@ -75,7 +75,7 @@ async function getAllItemsWithFlag() { const store = tx.objectStore('processes'); const request = store.openCursor(); const processes = []; - + request.onsuccess = (event) => { const cursor = event.target.result; if (cursor) { @@ -85,13 +85,13 @@ async function getAllItemsWithFlag() { resolve(processes); } }; - + request.onerror = (event) => { reject(event.target.error); }; }); }; - + const allProcesses = await getAllProcesses(); const tx = db.transaction('diffs', 'readonly'); const store = tx.objectStore('diffs'); @@ -100,13 +100,13 @@ async function getAllItemsWithFlag() { const request = store.getAll(); request.onsuccess = (event) => { const allItems = event.target.result; - const itemsWithFlag = allItems.filter(item => !item.need_validation); + const itemsWithFlag = allItems.filter((item) => item.need_validation); const processMap = {}; for (const diff of itemsWithFlag) { - const currentProcess = allProcesses.find(item => { - return item.states.some(state => state.merkle_root === diff.new_state_merkle_root); + const currentProcess = allProcesses.find((item) => { + return item.states.some((state) => state.merkle_root === diff.new_state_merkle_root); }); if (currentProcess) { @@ -116,18 +116,25 @@ async function getAllItemsWithFlag() { processMap[processKey] = { process: currentProcess.states, processId: currentProcess.key, - diffs: [] + diffs: [], }; } processMap[processKey].diffs.push(diff); } } - const results = Object.values(processMap).map(entry => { + const results = Object.values(processMap).map((entry) => { + const diffs = [] + for(const state of entry.process) { + const filteredDiff = entry.diffs.filter(diff => diff.new_state_merkle_root === state.merkle_root); + if(filteredDiff && filteredDiff.length) { + diffs.push(filteredDiff) + } + } return { process: entry.process, processId: entry.processId, - diffs: entry.diffs + diffs: diffs, }; }); @@ -139,4 +146,3 @@ async function getAllItemsWithFlag() { }; }); } - diff --git a/src/services/database.service.ts b/src/services/database.service.ts index 46a60e8..f40ca1f 100755 --- a/src/services/database.service.ts +++ b/src/services/database.service.ts @@ -1,4 +1,4 @@ -import Services from "./service"; +import Services from './service'; class Database { private static instance: Database; @@ -31,9 +31,9 @@ class Database { }, AnkDiff: { name: 'diffs', - options: { key: 'new_state_merkle_root'}, - indices: [] - } + options: { key: 'new_state_merkle_root' }, + indices: [], + }, }; // Private constructor to prevent direct instantiation from outside @@ -144,11 +144,11 @@ class Database { } private handleAddObjectResponse = async (event: MessageEvent) => { - const data = event.data + const data = event.data; console.log('Received response from service worker (ADD_OBJECT):', data); - if(data.type === 'NOTIFICATIONS') { - const service = await Services.getInstance() - service.setNotifications(data.data) + if (data.type === 'NOTIFICATIONS') { + const service = await Services.getInstance(); + service.setNotifications(data.data); } }; @@ -201,7 +201,7 @@ class Database { getRequest.onsuccess = () => resolve(getRequest.result); getRequest.onerror = () => reject(getRequest.error); }); - return result + return result; } public async dumpStore(storeName: string): Promise> { @@ -227,9 +227,8 @@ class Database { // Combine keys and values into an object const result: Record = Object.fromEntries(keys.map((key, index) => [key, values[index]])); return result; - } catch (error) { - console.error("Error fetching data from IndexedDB:", error); + console.error('Error fetching data from IndexedDB:', error); throw error; } } diff --git a/src/services/modal.service.ts b/src/services/modal.service.ts index 81a5f9c..0197416 100755 --- a/src/services/modal.service.ts +++ b/src/services/modal.service.ts @@ -2,9 +2,11 @@ import modalHtml from '../components/login-modal/login-modal.html?raw'; import modalScript from '../components/login-modal/login-modal.js?raw'; import validationModalStyle from '../components/validation-modal/validation-modal.css?raw'; import Services from './service'; -import { navigate } from '../router'; +import { init, navigate } from '../router'; import { addressToEmoji } from '../utils/sp-address.utils'; import { RoleDefinition } from 'pkg/sdk_client'; +import { initValidationModal } from '~/components/validation-modal/validation-modal'; +import { interpolate } from '~/utils/html.utils'; export default class ModalService { private static instance: ModalService; @@ -51,33 +53,35 @@ export default class ModalService { document.head.appendChild(script); } } - + async injectValidationModal(processDiff: any) { const container = document.querySelector('#containerId'); if (container) { let html = await fetch('/src/components/validation-modal/validation-modal.html').then((res) => res.text()); + html = interpolate(html, {processId: processDiff.processId}) container.innerHTML += html; // Dynamically load the header JS const script = document.createElement('script'); - script.id = 'validation-modal-script' + script.id = 'validation-modal-script'; script.src = '/src/components/validation-modal/validation-modal.ts'; script.type = 'module'; document.head.appendChild(script); const css = document.createElement('style'); - css.id = 'validation-modal-css' + css.id = 'validation-modal-css'; css.innerText = validationModalStyle; document.head.appendChild(css); + initValidationModal(processDiff) } } async closeValidationModal() { - const script = document.querySelector('#validation-modal-script') - const css = document.querySelector('#validation-modal-css') - const component = document.querySelector('#validation-modal') - script?.remove() - css?.remove() - component?.remove() + const script = document.querySelector('#validation-modal-script'); + const css = document.querySelector('#validation-modal-css'); + const component = document.querySelector('#validation-modal'); + script?.remove(); + css?.remove(); + component?.remove(); } // this is kind of too specific for pairing though @@ -88,7 +92,7 @@ export default class ModalService { const owner = roleDefinition['owner']; members = owner.members; } else { - throw new Error('No \"owner\" role'); + throw new Error('No "owner" role'); } // pairing specifics @@ -145,7 +149,7 @@ export default class ModalService { const createPrdUpdateReturn = service.createPrdUpdate(this.currentPcdCommitment); await service.handleApiReturn(createPrdUpdateReturn); } catch (e) { - throw e + throw e; } } else { throw new Error('No currentPcdCommitment'); @@ -156,7 +160,7 @@ export default class ModalService { const approveChangeReturn = service.approveChange(this.currentPcdCommitment!); await service.handleApiReturn(approveChangeReturn); } catch (e) { - throw e + throw e; } try { diff --git a/src/services/service.ts b/src/services/service.ts index dea691a..71ac46b 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -11,13 +11,15 @@ import { storeData, retrieveData } from './storage.service'; export const U32_MAX = 4294967295; const storageUrl = `http://localhost:8080`; +const RELAY_ADDRESS = 'sprt1qqdg4x69xdyhxpz4weuel0985qyswa0x9ycl4q6xc0fngf78jtj27gqj5vff4fvlt3fydx4g7vv0mh7vqv8jncgusp6n2zv860nufdzkyy59pqrdr'; +const wsurl = `https://demo.4nkweb.com/ws/`; export default class Services { private static initializing: Promise | null = null; private static instance: Services; private currentProcess: string | null = null; private pendingUpdates: any | null = null; - private currentUpdateMerkleRoot: string | null = null; + private currentUpdateMerkleRoot: string | null = null; private localAddress: string | null = null; private pairedAddresses: string[] = []; private sdkClient: any; @@ -95,7 +97,7 @@ export default class Services { throw new Error('Trying to connect to empty members list'); } - const members_str = members.map(member => JSON.stringify(member)); + const members_str = members.map((member) => JSON.stringify(member)); const waitForAmount = async (): Promise => { let attempts = 3; @@ -186,7 +188,7 @@ export default class Services { try { return this.sdkClient.create_response_prd(this.currentProcess, pcdMerkleRoot); } catch (e) { - throw e + throw e; } } @@ -282,7 +284,7 @@ export default class Services { object: secret, key: null, }); - } + } const entries = Object.entries(confirmedSecrets).map(([key, value]) => ({ key, value })); for (const entry of entries) { try { @@ -344,7 +346,7 @@ export default class Services { try { await this.openConfirmationModal(); } catch (e) { - throw new Error(`Error while evaluating pending updates for process ${this.currentProcess}: ${e}`) + throw new Error(`Error while evaluating pending updates for process ${this.currentProcess}: ${e}`); } } @@ -365,7 +367,7 @@ export default class Services { continue; } const actualProposal: Record = JSON.parse(value.new_value); - const merkleRoot: string = value.new_state_merkle_root; + const merkleRoot: string = value.new_state_merkle_root; try { await this.routingInstance.openPairingConfirmationModal(actualProposal, this.currentProcess!, merkleRoot); } catch (e) { @@ -479,7 +481,7 @@ export default class Services { key: commitedIn, }); } catch (e) { - throw new Error(`Failed to save process: ${e}`) + throw new Error(`Failed to save process: ${e}`); } // We check how many copies in storage nodes @@ -502,7 +504,7 @@ export default class Services { public async saveDiffs(diffs: UserDiff[]) { const db = await Database.getInstance(); try { - for(const diff of diffs) { + for (const diff of diffs) { await db.addObject({ storeName: 'diffs', object: diff, @@ -510,7 +512,7 @@ export default class Services { }); } } catch (e) { - throw new Error(`Failed to save process: ${e}`) + throw new Error(`Failed to save process: ${e}`); } } @@ -540,7 +542,6 @@ export default class Services { } catch (e) { throw e; } - } public async restoreSecrets() { @@ -549,14 +550,13 @@ export default class Services { const sharedSecrets: Record = await db.dumpStore('shared_secrets'); const unconfirmedSecrets = await db.dumpStore('unconfirmed_secrets'); const secretsStore = { - 'shared_secrets': sharedSecrets, - 'unconfirmed_secrets': Object.values(unconfirmedSecrets), + shared_secrets: sharedSecrets, + unconfirmed_secrets: Object.values(unconfirmedSecrets), }; this.sdkClient.set_shared_secrets(JSON.stringify(secretsStore)); } catch (e) { throw e; } - } getNotifications(): any[] | null { @@ -583,13 +583,13 @@ export default class Services { // path: '/notif3', // }, // ]; - return this.notifications + return this.notifications; } setNotifications(notifications: any[]) { - this.notifications = notifications + this.notifications = notifications; } - + async importJSON(content: any): Promise { return Promise.resolve(); } diff --git a/src/utils/document.utils.ts b/src/utils/document.utils.ts index b0a1005..f8dab37 100644 --- a/src/utils/document.utils.ts +++ b/src/utils/document.utils.ts @@ -1,4 +1,4 @@ -export function getCorrectDOM(componentTag: string): Node { - const dom = document?.querySelector(componentTag)?.shadowRoot || document as Node - return dom - } \ No newline at end of file +export function getCorrectDOM(componentTag: string): Node { + const dom = document?.querySelector(componentTag)?.shadowRoot || (document as Node); + return dom; +} diff --git a/src/utils/html.utils.ts b/src/utils/html.utils.ts index e8620b9..1a114eb 100755 --- a/src/utils/html.utils.ts +++ b/src/utils/html.utils.ts @@ -3,6 +3,6 @@ export function interpolate(template: string, data: { [key: string]: string }) { } export function getCorrectDOM(componentTag: string): Node { - const dom = document?.querySelector(componentTag)?.shadowRoot || document as Node - return dom -} \ No newline at end of file + const dom = document?.querySelector(componentTag)?.shadowRoot || (document as Node); + return dom; +} diff --git a/src/utils/messageMock.ts b/src/utils/messageMock.ts index 0619b53..cae381f 100755 --- a/src/utils/messageMock.ts +++ b/src/utils/messageMock.ts @@ -1,53 +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 +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(); diff --git a/src/utils/notification.store.ts b/src/utils/notification.store.ts index 8b1002c..88c5caf 100755 --- a/src/utils/notification.store.ts +++ b/src/utils/notification.store.ts @@ -1,96 +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 +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(); diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts index 68e8e99..05edba9 100755 --- a/src/utils/sp-address.utils.ts +++ b/src/utils/sp-address.utils.ts @@ -100,7 +100,7 @@ export async function displayEmojis(text: string) { // Verify Other address export function initAddressInput() { - const container = getCorrectDOM('login-4nk-component') as HTMLElement + const container = getCorrectDOM('login-4nk-component') as HTMLElement; const addressInput = container.querySelector('#addressInput') as HTMLInputElement; const emojiDisplay = container.querySelector('#emoji-display-2'); const okButton = container.querySelector('#okButton') as HTMLButtonElement; @@ -148,13 +148,13 @@ export function initAddressInput() { } async function onOkButtonClick() { - const container = getCorrectDOM('login-4nk-component') as HTMLElement + const container = getCorrectDOM('login-4nk-component') as HTMLElement; const secondDeviceAddress = (container.querySelector('#addressInput') as HTMLInputElement).value; try { // Connect to target, if necessary await prepareAndSendPairingTx(secondDeviceAddress); } catch (e) { - console.error(`onOkButtonClick error: ${e}`); + console.error(`onOkButtonClick error: ${e}`); } } @@ -184,7 +184,7 @@ export async function prepareAndSendPairingTx(secondDeviceAddress: string) { export async function generateQRCode(spAddress: string) { try { - const container = getCorrectDOM('login-4nk-component') as HTMLElement + const container = getCorrectDOM('login-4nk-component') as HTMLElement; const currentUrl = 'https://' + window.location.host; const url = await QRCode.toDataURL(currentUrl + '?sp_address=' + spAddress); const qrCode = container?.querySelector('.qr-code img');