diff --git a/package.json b/package.json index e9bc61e..0dbc75c 100755 --- a/package.json +++ b/package.json @@ -16,25 +16,12 @@ "author": "", "license": "ISC", "devDependencies": { - "@rollup/plugin-typescript": "^12.1.1", - "copy-webpack-plugin": "^12.0.2", - "html-webpack-plugin": "^5.6.0", "prettier": "^3.3.3", - "rimraf": "^6.0.1", - "ts-loader": "^9.5.1", "typescript": "^5.3.3", "vite": "^5.4.11", - "vite-plugin-static-copy": "^1.0.6", - "webpack": "^5.90.3", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^5.0.2" + "vite-plugin-static-copy": "^1.0.6" }, "dependencies": { - "@angular/elements": "^19.0.1", - "@types/jsonwebtoken": "^9.0.9", - "@types/qrcode": "^1.5.5", - "@vitejs/plugin-react": "^4.3.1", - "@vitejs/plugin-vue": "^5.0.5", "axios": "^1.7.8", "html5-qrcode": "^2.3.8", "jose": "^6.0.11", diff --git a/src/main.ts b/src/main.ts index 3c59392..fd5a008 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,23 +1 @@ -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';*/ -export { SignatureComponent, SignatureElement }; - -declare global { - interface HTMLElementTagNameMap { - 'signature-component': SignatureComponent; - 'signature-element': SignatureElement; - /*'chat-component': ChatComponent; - 'chat-element': ChatElement;*/ - } -} - -// 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);*/ -} +// Main entry point - no custom elements needed for current implementation diff --git a/src/mocks/mock-chat/groupsMock.js b/src/mocks/mock-chat/groupsMock.js deleted file mode 100755 index d5d1fcf..0000000 --- a/src/mocks/mock-chat/groupsMock.js +++ /dev/null @@ -1,52 +0,0 @@ -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 deleted file mode 100755 index 28792d9..0000000 --- a/src/mocks/mock-chat/messagesMock.js +++ /dev/null @@ -1,64 +0,0 @@ -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 deleted file mode 100755 index c652112..0000000 --- a/src/mocks/mock-signature/groupsMock.js +++ /dev/null @@ -1,471 +0,0 @@ -// 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 deleted file mode 100755 index 2ffc6ce..0000000 --- a/src/mocks/mock-signature/membersMocks.js +++ /dev/null @@ -1,105 +0,0 @@ -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 deleted file mode 100755 index ca1c31e..0000000 --- a/src/mocks/mock-signature/messagesMock.ts +++ /dev/null @@ -1,64 +0,0 @@ -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/pages/account/document-validation.ts b/src/pages/account/document-validation.ts deleted file mode 100644 index 0fe233a..0000000 --- a/src/pages/account/document-validation.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { ProcessState } from '../../../pkg/sdk_client'; -import Services from '../../services/service'; - -interface State { - file: File | null; - fileHash: string | null; - certificate: ProcessState | null; - commitmentHashes: string[]; -} - -export interface Vin { - txid: string; // The txid of the previous transaction (being spent) - vout: number; // The output index in the previous tx - prevout: { - scriptpubkey: string; - scriptpubkey_asm: string; - scriptpubkey_type: string; - scriptpubkey_address: string; - value: number; - }; - scriptsig: string; - scriptsig_asm: string; - witness: string[]; - is_coinbase: boolean; - sequence: number; -} - -export interface TransactionInfo { - txid: string; - version: number; - locktime: number; - vin: Vin[]; - vout: any[]; - size: number; - weight: number; - fee: number; - status: { - confirmed: boolean; - block_height: number; - block_hash: string; - block_time: number; - }; -} - -export function getDocumentValidation(container: HTMLElement) { - const state: State = { - file: null, - fileHash: null, - certificate: null, - commitmentHashes: [] - } - - container.innerHTML = ''; - container.style.cssText = ` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - min-height: 100vh; - gap: 2rem; - `; - - function createDropButton( - label: string, - onDrop: (file: File, updateVisuals: (file: File) => void) => void, - accept: string = '*/*' - ): HTMLElement { - const wrapper = document.createElement('div'); - wrapper.style.cssText = ` - width: 200px; - height: 100px; - border: 2px dashed #888; - border-radius: 8px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - cursor: pointer; - font-weight: bold; - background: #f8f8f8; - text-align: center; - padding: 0.5rem; - box-sizing: border-box; - `; - - const title = document.createElement('div'); - title.textContent = label; - - const filename = document.createElement('div'); - filename.style.cssText = ` - font-size: 0.85rem; - margin-top: 0.5rem; - color: #444; - word-break: break-word; - text-align: center; - `; - - wrapper.appendChild(title); - wrapper.appendChild(filename); - - const updateVisuals = (file: File) => { - wrapper.style.borderColor = 'green'; - wrapper.style.background = '#e6ffed'; - filename.textContent = file.name; - }; - - // === Hidden file input === - const fileInput = document.createElement('input'); - fileInput.type = 'file'; - fileInput.accept = accept; - fileInput.style.display = 'none'; - document.body.appendChild(fileInput); - - fileInput.onchange = () => { - const file = fileInput.files?.[0]; - if (file) { - onDrop(file, updateVisuals); - fileInput.value = ''; // reset so same file can be re-selected - } - }; - - // === Handle drag-and-drop === - wrapper.ondragover = e => { - e.preventDefault(); - wrapper.style.background = '#e0e0e0'; - }; - - wrapper.ondragleave = () => { - wrapper.style.background = '#f8f8f8'; - }; - - wrapper.ondrop = e => { - e.preventDefault(); - wrapper.style.background = '#f8f8f8'; - - const file = e.dataTransfer?.files?.[0]; - if (file) { - onDrop(file, updateVisuals); - } - }; - - // === Handle click to open file manager === - wrapper.onclick = () => { - fileInput.click(); - }; - - return wrapper; - } - - const fileDropButton = createDropButton('Drop file', async (file, updateVisuals) => { - try { - state.file = file; - updateVisuals(file); - console.log('Loaded file:', state.file); - checkReady(); - } catch (err) { - alert('Failed to drop the file.'); - console.error(err); - } - }); - - const certDropButton = createDropButton('Drop certificate', async (file, updateVisuals) => { - try { - const text = await file.text(); - const json = JSON.parse(text); - if ( - typeof json === 'object' && - json !== null && - typeof json.pcd_commitment === 'object' && - typeof json.state_id === 'string' - ) { - state.certificate = json as ProcessState; - - state.commitmentHashes = Object.values(json.pcd_commitment).map((h: string) => - h.toLowerCase() - ); - - updateVisuals(file); - console.log('Loaded certificate, extracted hashes:', state.commitmentHashes); - checkReady(); - } else { - alert('Invalid certificate structure.'); - } - } catch (err) { - alert('Failed to parse certificate JSON.'); - console.error(err); - } - }); - - const buttonRow = document.createElement('div'); - buttonRow.style.display = 'flex'; - buttonRow.style.gap = '2rem'; - buttonRow.appendChild(fileDropButton); - buttonRow.appendChild(certDropButton); - - container.appendChild(buttonRow); - - async function checkReady() { - if (state.file && state.certificate && state.commitmentHashes.length > 0) { - // We take the commited_in and all pcd_commitment keys to reconstruct all the possible hash - const fileBlob = { - type: state.file.type, - data: new Uint8Array(await state.file.arrayBuffer()) - }; - const service = await Services.getInstance(); - const commitedIn = state.certificate.commited_in; - if (!commitedIn) return; - const [prevTxid, prevTxVout] = commitedIn.split(':'); - const processId = state.certificate.process_id; - const stateId = state.certificate.state_id; - const process = await service.getProcess(processId); - if (!process) return; - - // Get the transaction that comes right after the commited_in - const nextState = service.getNextStateAfterId(process, stateId); - - if (!nextState) { - alert(`❌ Validation failed: No next state, is the state you're trying to validate commited?`); - return; - } - - const [outspentTxId, _] = nextState.commited_in.split(':'); - console.log(outspentTxId); - - // Check that the commitment transaction exists, and that it commits to the state id - - const txInfo = await fetchTransaction(outspentTxId); - if (!txInfo) { - console.error(`Validation error: Can't fetch new state commitment transaction`); - alert(`❌ Validation failed: invalid or non existent commited_in for state ${stateId}.`); - return; - } - - // We must check that this transaction indeed spend the commited_in we have in the certificate - let found = false; - for (const vin of txInfo.vin) { - if (vin.txid === prevTxid) { - found = true; - break; - } - } - - if (!found) { - console.error(`Validation error: new state doesn't spend previous state commitment transaction`); - alert('❌ Validation failed: Unconsistent commitment transactions history.'); - return; - } - - // set found back to false for next check - found = false; - - // is the state_id commited in the transaction? - for (const vout of txInfo.vout) { - console.log(vout); - if (vout.scriptpubkey_type && vout.scriptpubkey_type === 'op_return') { - found = true; - } else { - continue; - } - - if (vout.scriptpubkey_asm) { - const hash = extractHexFromScriptAsm(vout.scriptpubkey_asm); - if (hash) { - if (hash !== stateId) { - console.error(`Validation error: expected stateId ${stateId}, got ${hash}`); - alert('❌ Validation failed: Transaction does not commit to that state.'); - return; - } - } - } - } - - if (!found) { - alert('❌ Validation failed: Transaction does not contain data.'); - return; - } - - // set found back to false for next check - found = false; - - for (const label of Object.keys(state.certificate.pcd_commitment)) { - // Compute the hash for this label - console.log(`Computing hash with label ${label}`) - const fileHex = service.getHashForFile(commitedIn, label, fileBlob); - console.log(`Found hash ${fileHex}`); - found = state.commitmentHashes.includes(fileHex); - if (found) break; - } - - if (found) { - alert('✅ Validation successful: file hash found in pcd_commitment.'); - } else { - alert('❌ Validation failed: file hash NOT found in pcd_commitment.'); - } - } - } - - async function fetchTransaction(txid: string): Promise { - const url = `https://mempool.4nkweb.com/api/tx/${txid}`; - - const response = await fetch(url); - if (!response.ok) { - throw new Error(`Failed to fetch outspend status: ${response.statusText}`); - } - - const outspend: TransactionInfo = await response.json(); - return outspend; - } - - function extractHexFromScriptAsm(scriptAsm: string): string | null { - const parts = scriptAsm.trim().split(/\s+/); - const last = parts[parts.length - 1]; - - // Basic validation: must be 64-char hex (32 bytes) - if (/^[0-9a-fA-F]{64}$/.test(last)) { - return last.toLowerCase(); - } - - return null; - } -} diff --git a/src/pages/account/key-value-section.ts b/src/pages/account/key-value-section.ts deleted file mode 100644 index a43bfad..0000000 --- a/src/pages/account/key-value-section.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { ValidationRule, RoleDefinition } from '../../../pkg/sdk_client'; -import { showValidationRuleModal } from '../../components/validation-rule-modal/validation-rule-modal'; - -export function createKeyValueSection(title: string, id: string, isRoleSection = false) { - const section = document.createElement('div'); - section.id = id; - section.style.cssText = 'margin-bottom: 2rem; background: #fff; padding: 1rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);'; - - const titleEl = document.createElement('h2'); - titleEl.textContent = title; - titleEl.style.cssText = 'font-size: 1.25rem; font-weight: bold; margin-bottom: 1rem;'; - section.appendChild(titleEl); - - const rowContainer = document.createElement('div'); - section.appendChild(rowContainer); - - const addBtn = document.createElement('button'); - addBtn.textContent = '+ Add Row'; - addBtn.style.cssText = ` - margin-top: 1rem; - padding: 0.5rem 1rem; - border: 1px solid #888; - border-radius: 0.375rem; - background-color: #f9f9f9; - cursor: pointer; - `; - section.appendChild(addBtn); - - const roleRowStates: { - roleNameInput: HTMLInputElement; - membersInput: HTMLInputElement; - storagesInput: HTMLInputElement; - validationRules: ValidationRule[]; - }[] = []; - type fileBlob = { - type: string, - data: Uint8Array - }; - const nonRoleRowStates: { - keyInput: HTMLInputElement, - valueInput: HTMLInputElement, - fileInput: HTMLInputElement, - fileBlob: fileBlob | null - }[] = []; - - const inputStyle = 'flex: 1; height: 2.5rem; padding: 0.5rem; border: 1px solid #ccc; border-radius: 0.375rem;'; - - const createRow = () => { - const row = document.createElement('div'); - row.style.cssText = 'display: flex; gap: 1rem; margin-bottom: 0.5rem; align-items: center;'; - - const deleteBtn = document.createElement('button'); - deleteBtn.textContent = '🗑️'; - deleteBtn.style.cssText = 'background: none; border: none; font-size: 1.2rem; cursor: pointer;'; - deleteBtn.onclick = () => { - row.remove(); - updateDeleteButtons(); - }; - - if (isRoleSection) { - const roleName = document.createElement('input'); - const members = document.createElement('input'); - const storages = document.createElement('input'); - - roleName.placeholder = 'Role name'; - members.placeholder = 'members'; - storages.placeholder = 'storages'; - [roleName, members, storages].forEach(input => { - input.type = 'text'; - input.style.cssText = inputStyle; - }); - - const ruleButton = document.createElement('button'); - ruleButton.textContent = 'Add Validation Rule'; - ruleButton.style.cssText = 'padding: 0.3rem 0.75rem; border: 1px solid #ccc; border-radius: 0.375rem; background: #f0f0f0; cursor: pointer;'; - - const rules: ValidationRule[] = []; - ruleButton.onclick = () => { - showValidationRuleModal(rule => { - rules.push(rule); - ruleButton.textContent = `Rules (${rules.length})`; - }); - }; - - row.appendChild(roleName); - row.appendChild(members); - row.appendChild(storages); - row.appendChild(ruleButton); - row.appendChild(deleteBtn); - - roleRowStates.push({ roleNameInput: roleName, membersInput: members, storagesInput: storages, validationRules: rules }); - } else { - const fileInput = document.createElement('input'); - fileInput.type = 'file'; - fileInput.style.display = 'none'; - fileInput.onchange = async () => { - const file = fileInput.files?.[0]; - if (!file) return; - - const buffer = await file.arrayBuffer(); - const uint8 = new Uint8Array(buffer); - - rowState.fileBlob = { - type: file.type, - data: uint8, - }; - - valueInput.value = `📄 ${file.name}`; - valueInput.disabled = true; - attachBtn.textContent = `📎 ${file.name}`; - }; - - const attachBtn = document.createElement('button'); - attachBtn.textContent = '📎 Attach'; - attachBtn.style.cssText = 'padding: 0.3rem 0.75rem; border: 1px solid #ccc; border-radius: 0.375rem; background: #f0f0f0; cursor: pointer;'; - attachBtn.onclick = () => fileInput.click(); - - const keyInput = document.createElement('input'); - const valueInput = document.createElement('input'); - - const rowState = { - keyInput, - valueInput, - fileInput, - fileBlob: null as fileBlob | null - }; - nonRoleRowStates.push(rowState); - - keyInput.placeholder = 'Key'; - valueInput.placeholder = 'Value'; - [keyInput, valueInput].forEach(input => { - input.type = 'text'; - input.style.cssText = inputStyle; - }); - - row.appendChild(keyInput); - row.appendChild(valueInput); - - row.appendChild(attachBtn); - row.appendChild(fileInput); - - row.appendChild(deleteBtn); - } - - rowContainer.appendChild(row); - updateDeleteButtons(); - }; - - const updateDeleteButtons = () => { - const rows = Array.from(rowContainer.children); - rows.forEach(row => { - const btn = row.querySelector('button:last-child') as HTMLButtonElement; - if (rows.length === 1) { - btn.disabled = true; - btn.style.visibility = 'hidden'; - } else { - btn.disabled = false; - btn.style.visibility = 'visible'; - } - }); - }; - - createRow(); - addBtn.addEventListener('click', createRow); - - return { - element: section, - getData: () => { - if (isRoleSection) { - const data: Record = {}; - for (const row of roleRowStates) { - const key = row.roleNameInput.value.trim(); - if (!key) continue; - data[key] = { - members: row.membersInput.value.split(',').map(x => x.trim()).filter(Boolean), - storages: row.storagesInput.value.split(',').map(x => x.trim()).filter(Boolean), - validation_rules: row.validationRules - }; - } - return data; - } else { - const data: Record = {}; - for (const row of nonRoleRowStates) { - const key = row.keyInput.value.trim(); - if (!key) continue; - if (row.fileBlob) { - data[key] = row.fileBlob; - } else { - data[key] = row.valueInput.value.trim(); - } - } - return data; - } - } - }; -} diff --git a/src/pages/account/process-creation.ts b/src/pages/account/process-creation.ts deleted file mode 100644 index 586a108..0000000 --- a/src/pages/account/process-creation.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { createKeyValueSection } from './key-value-section'; -import { loadValidationRuleModal } from '../../components/validation-rule-modal/validation-rule-modal'; -import Services from '../../services/service'; -import { RoleDefinition } from '../../../pkg/sdk_client'; - -export async function getProcessCreation(container: HTMLElement) { - await loadValidationRuleModal(); - - container.style.display = 'block'; - container.innerHTML = `
Process Creation
`; - const privateSec = createKeyValueSection('Private Data', 'private-section'); - const publicSec = createKeyValueSection('Public Data', 'public-section'); - const rolesSec = createKeyValueSection('Roles', 'roles-section', true); - - container.appendChild(privateSec.element); - container.appendChild(publicSec.element); - container.appendChild(rolesSec.element); - - const btn = document.createElement('button'); - btn.textContent = 'Create Process'; - btn.style.cssText = ` - display: block; - margin: 2rem auto 0; - padding: 0.75rem 2rem; - font-size: 1rem; - font-weight: bold; - background-color: #4f46e5; - color: white; - border: none; - border-radius: 0.5rem; - cursor: pointer; - `; - - btn.onclick = async () => { - const privateData = privateSec.getData(); - const publicData = publicSec.getData(); - const roles = rolesSec.getData() as Record; - - console.log('Private:', privateData); - console.log('Public:', publicData); - console.log('Roles:', roles); - - const service = await Services.getInstance(); - - const createProcessResult = await service.createProcess(privateData, publicData, roles); - const processId = createProcessResult.updated_process!.process_id; - const stateId = createProcessResult.updated_process!.current_process.states[0].state_id; - await service.handleApiReturn(createProcessResult); - - // Now we want to validate the update and register the first state of our new process - const updateProcessResult = await service.createPrdUpdate(processId, stateId); - await service.handleApiReturn(createProcessResult); - - const approveChangeResult = await service.approveChange(processId, stateId); - await service.handleApiReturn(approveChangeResult); - if (approveChangeResult) { - const process = await service.getProcess(processId); - let newState = service.getStateFromId(process, stateId); - if (!newState) return; - for (const label of Object.keys(newState.keys)) { - const hash = newState.pcd_commitment[label]; - const encryptedData = await service.getBlobFromDb(hash); - const filename = `${label}-${hash.slice(0,8)}.bin`; - - const blob = new Blob([encryptedData], { type: "application/octet-stream" }); - const link = document.createElement("a"); - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); - - setTimeout(() => URL.revokeObjectURL(link.href), 1000); - } - - await service.generateProcessPdf(processId, newState); - - // Add processId to the state we export - newState['process_id'] = processId; - const blob = new Blob([JSON.stringify(newState, null, 2)], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - - const a = document.createElement('a'); - a.href = url; - a.download = `process_${processId}_${stateId}.json`; - a.click(); - - URL.revokeObjectURL(url); // Clean up - } - }; - - container.appendChild(btn); -} diff --git a/src/pages/account/process.ts b/src/pages/account/process.ts deleted file mode 100644 index 10eaeb6..0000000 --- a/src/pages/account/process.ts +++ /dev/null @@ -1,66 +0,0 @@ -export function createProcessTab(container: HTMLElement, processes: { name: string, publicData: Record }[]): HTMLElement { - container.id = 'process-tab'; - container.style.display = 'block'; - container.style.cssText = 'padding: 1.5rem;'; - - const title = document.createElement('h2'); - title.textContent = 'Processes'; - title.style.cssText = 'font-size: 1.5rem; font-weight: bold; margin-bottom: 1rem;'; - container.appendChild(title); - - processes.forEach(proc => { - const card = document.createElement('div'); - card.style.cssText = 'margin-bottom: 1rem; padding: 1rem; border: 1px solid #ddd; border-radius: 0.5rem; background: #fff;'; - - const nameEl = document.createElement('h3'); - nameEl.textContent = proc.name; - nameEl.style.cssText = 'font-size: 1.2rem; font-weight: bold; margin-bottom: 0.5rem;'; - card.appendChild(nameEl); - - const dataList = document.createElement('div'); - for (const [key, value] of Object.entries(proc.publicData)) { - const item = document.createElement('div'); - item.style.cssText = 'margin-bottom: 0.5rem;'; - - const label = document.createElement('strong'); - label.textContent = key + ': '; - item.appendChild(label); - - // Let's trim the quotes - const trimmed = value.replace(/^'|'$/g, ''); - let parsed; - try { - parsed = JSON.parse(trimmed); - } catch (_) { - parsed = trimmed; - } - - if (parsed && typeof parsed === 'object') { - const saveBtn = document.createElement('button'); - saveBtn.textContent = '💾 Save as JSON'; - saveBtn.style.cssText = 'margin-left: 0.5rem; padding: 0.25rem 0.5rem; border: 1px solid #ccc; border-radius: 0.375rem; background: #f0f0f0; cursor: pointer;'; - saveBtn.onclick = () => { - const blob = new Blob([JSON.stringify(parsed, null, 2)], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `${proc.name}_${key}.json`; - a.click(); - URL.revokeObjectURL(url); - }; - item.appendChild(saveBtn); - } else { - const span = document.createElement('span'); - span.textContent = String(parsed); - item.appendChild(span); - } - - dataList.appendChild(item); - } - - card.appendChild(dataList); - container.appendChild(card); - }); - - return container; -} diff --git a/src/pages/chat/chat-component.ts b/src/pages/chat/chat-component.ts deleted file mode 100644 index 2ded0b8..0000000 --- a/src/pages/chat/chat-component.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*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(); - - if (!customElements.get('chat-element')) { - customElements.define('chat-element', ChatElement); - } - } - - 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 deleted file mode 100755 index 5265c88..0000000 --- a/src/pages/chat/chat.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-
    -
-
- - -
-
- -
-
- -
- - -
- - - - -
-
- - -
-
-

Signature

- -
-
-
-

Description

-
-
-
-

Documents

- -
-
-
    -
    -
    -
    -
    - - `; - window.loadMemberChat = async (memberId: string | number) => { - if (typeof memberId === 'string') { - return await this.loadMemberChat(memberId); - } else { - console.error('Invalid memberId type. Expected string, got number.'); - } - }; - - document.addEventListener('newDataReceived', async (event: CustomEvent) => { - const { detail } = event; - console.log('New data event received:', JSON.stringify(detail)); - - if (detail.processId && detail.processId === this.selectedChatProcessId) { - console.log('Detected update to chat'); - if (this.selectedMember) { - await this.loadMemberChat(this.selectedMember); - } else { - console.error('No selected member?'); - } - } else { - console.log('Received an update for another process'); - } - }); - - - document.addEventListener('DOMContentLoaded', () => { - this.notificationBadge = document.querySelector('.notification-badge'); - this.notificationBoard = document.getElementById('notification-board'); - this.notificationBell = document.getElementById('notification-bell'); - - if (!this.notificationBadge || !this.notificationBoard || !this.notificationBell) { - console.error('Notification elements not found'); - } - }); - - // 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(); - - } - - private initMessageEvents() { - const sendButton = this.shadowRoot?.querySelector('#send-button'); - if (sendButton) { - sendButton.addEventListener('click', async () => { - await this.sendMessage(); - setTimeout(async () => await this.reloadMemberChat(this.selectedMember), 600); - messageInput.value = ''; - }); - } - - const messageInput = this.shadowRoot?.querySelector('#message-input'); - if (messageInput) { - messageInput.addEventListener('keypress', async (event: Event) => { - const keyEvent = event as KeyboardEvent; - if (keyEvent.key === 'Enter' && !keyEvent.shiftKey) { - event.preventDefault(); - await this.sendMessage(); - setTimeout(async () => await this.reloadMemberChat(this.selectedMember), 600); - messageInput.value = ''; - } - }); - } - - const fileInput = this.shadowRoot?.querySelector('#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 = async () => { - await this.loadMemberChat(notif.memberId); - await 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 async addNotification(memberId: string, message: any) { - try { - // Obtenir l'emoji à partir du Pairing Process - const pairingProcess = await this.getPairingProcess(memberId); - const memberEmoji = await addressToEmoji(pairingProcess); - - // Obtenir le processus et le rôle - const processId = this.getAttribute('process-id'); - const processEmoji = processId ? await addressToEmoji(processId) : '📝'; - - // Trouver le rôle du membre - const member = this.allMembers.find(m => String(m.id) === memberId); - const role = message.metadata?.roleName || 'Member'; - - // Déterminer le texte de la notification - let notificationText = ''; - if (message.type === 'file') { - notificationText = `${memberEmoji} (${role}) in ${processEmoji}: New file - ${message.fileName}`; - } else { - notificationText = `${memberEmoji} (${role}) in ${processEmoji}: ${message.metadata.text}`; - } - - // Créer la notification - const notification = { - memberId, - text: notificationText, - time: new Date(message.metadata.timestamp).toLocaleString('fr-FR') - }; - - // Ajouter la notification et mettre à jour l'interface - this.notifications.push(notification); - this.renderNotifications(); - this.updateNotificationBadge(); - - } catch (error) { - console.error('Error creating notification:', error); - } - } - - private async sendMessage() { - const messageInput = this.shadowRoot?.querySelector('#message-input') as HTMLInputElement; - if (!messageInput || !this.selectedMember) { - console.error('❌ Missing message input or selected member'); - return; - } - - if (!this.selectedChatProcessId) { - console.error('no process id set'); - return; - } - - const messageText = messageInput.value.trim(); - if (messageText === '') { - console.error('❌ Empty message'); - return; - } - - try { - const service = await Services.getInstance(); - const myProcessId = await this.getMyProcessId(); - - if (!myProcessId) { - console.error('No paired member found'); - return; - } - - const timestamp = Date.now(); - const message = { - state: this.messageState, - type: 'text', - content: messageText, - metadata: { - createdAt: timestamp, - lastModified: timestamp, - sender: myProcessId, - recipient: this.selectedMember, - } - }; - - console.log("----this.selectedChatProcessId",this.selectedChatProcessId ); - const process = await service.getProcess(this.selectedChatProcessId); - - if (!process) { - console.error('Failed to retrieve process from DB'); - return; - } - - // For a dm process, there are only 2 attributes, description will stay the same, message is the new message - // We don't need to get previous values for now, so let's just skip it - let newState = { - message: message, - description: 'dm' - }; - - // Now we create a new state for the dm process - let apiReturn; - try { - console.log(process); - apiReturn = await service.updateProcess(process, newState, null); - } catch (e) { - console.error('Failed to update process:', e); - return; - } - const updatedProcess = apiReturn.updated_process.current_process; - const newStateId = updatedProcess.states[updatedProcess.states.length - 2 ].state_id; // We take the last concurrent state, just before the tip - console.log(`newStateId: ${newStateId}`); - await service.handleApiReturn(apiReturn); - - const createPrdReturn = service.createPrdUpdate(this.selectedChatProcessId, newStateId); - await service.handleApiReturn(createPrdReturn); - - // Now we validate the new state - const approveChangeReturn = await service.approveChange(this.selectedChatProcessId, newStateId); - await service.handleApiReturn(approveChangeReturn); - - await this.lookForMyDms(); - - const groupList = this.shadowRoot?.querySelector('#group-list'); - const tabs = this.shadowRoot?.querySelectorAll('.tab'); - const memberList = groupList?.querySelector('.member-list'); - - if (memberList) { - memberList.innerHTML = ''; - await this.loadAllMembers(); - if (tabs) { - await this.switchTab('members', tabs); - } - } - } catch (error) { - console.error('❌ Error in sendMessage:', error); - } - } - - private scrollToBottom(container: Element) { - (container as HTMLElement).scrollTop = (container as HTMLElement).scrollHeight; - } - - // Get the diff by state id - async getDiffByStateId(stateId: string) { - try { - const database = await Database.getInstance(); - const diff = await database.requestStoreByIndex('diffs', 'byStateId', stateId); - return diff; - } catch (error) { - console.error('Error getting diff by state id:', error); - } - } - - // TODO rewrite that - // private async lookForChildren(): Promise { - // // Filter processes for the children of current process - // const service = await Services.getInstance(); - // if (!this.selectedChatProcessId) { - // console.error('No process id'); - // return null; - // } - // const children: string[] = await service.getChildrenOfProcess(this.selectedChatProcessId); - - // const processRoles = this.processRoles; - // const selectedMember = this.selectedMember; - // for (const child of children) { - // const roles = service.getRoles(JSON.parse(child)); - // // Check that we and the other members are in the role - // if (!service.isChildRole(processRoles, roles)) { - // console.error('Child process roles are not a subset of parent') - // continue; - // } - // if (!service.rolesContainsMember(roles, selectedMember)) { - // console.error('Member is not part of the process'); - // continue; - // } - // if (!service.rolesContainsUs(roles)) { - // console.error('We\'re not part of child process'); - // continue; - // } - // return child; - // } - - // return null; - // } - - private async loadAllMembers() { - const groupList = this.shadowRoot?.querySelector('#group-list'); - if (!groupList) return; - - const service = await Services.getInstance(); - const members = await service.getAllMembers(); - const processes = await service.getProcesses(); - - const memberList = document.createElement('ul'); - memberList.className = 'member-list active'; - - // Partition members into prioritized and remaining arrays. - const prioritizedMembers: [string, Member][] = []; - const remainingMembers: [string, Member][] = []; - for (const [processId, member] of Object.entries(members)) { - if (this.dmMembersSet.has(processId)) { - prioritizedMembers.push([processId, member]); - } else { - remainingMembers.push([processId, member]); - } - } - const sortedMembers = prioritizedMembers.concat(remainingMembers); - - // Process each member. - for (const [processId, member] of sortedMembers) { - const memberItem = document.createElement('li'); - memberItem.className = 'member-item'; - - // Apply special styling if the member is prioritized. - if (this.dmMembersSet.has(processId)) { - memberItem.style.cssText = ` - background-color: var(--accent-color); - transition: background-color 0.3s ease; - cursor: pointer; - `; - memberItem.addEventListener('mouseover', () => { - memberItem.style.backgroundColor = 'var(--accent-color-hover)'; - }); - memberItem.addEventListener('mouseout', () => { - memberItem.style.backgroundColor = 'var(--accent-color)'; - }); - } - - // Create a container for the member content. - const memberContainer = document.createElement('div'); - memberContainer.className = 'member-container'; - - // Create the emoji span and load its label. - const emojiSpan = document.createElement('span'); - emojiSpan.className = 'member-emoji'; - const emojis = await addressToEmoji(processId); - emojiSpan.dataset.emojis = emojis; - - // Get the member name, if any, and add it to the display - const process = processes[processId]; - let memberPublicName; - if (process) { - const publicMemberData = service.getPublicData(process); - if (publicMemberData) { - const extractedName = publicMemberData['memberPublicName']; - if (extractedName !== undefined && extractedName !== null) { - memberPublicName = extractedName; - } - } - } - if (!memberPublicName) { - memberPublicName = 'Unnamed Member'; - } - - emojiSpan.textContent = `${memberPublicName} (${emojis})` - - memberContainer.appendChild(emojiSpan); - memberItem.appendChild(memberContainer); - - // Add click handler to load member chat. - memberItem.addEventListener('click', async () => { - await this.loadMemberChat(processId); - }); - - // Create and configure the edit label button. - const editLabelButton = document.createElement('button'); - editLabelButton.className = 'edit-label-button'; - editLabelButton.textContent = "✏️"; - editLabelButton.addEventListener("click", (event) => { - event.stopPropagation(); - }); - editLabelButton.addEventListener("dblclick", async (event) => { - event.stopPropagation(); - event.preventDefault(); - - const newLabel = prompt("Set a new name for the member:"); - if (!newLabel) return; - - const db = await Database.getInstance(); - this.updateLabelForEmoji(emojis, newLabel, db, emojiSpan, processId); - }); - memberContainer.appendChild(editLabelButton); - - memberList.appendChild(memberItem); - } - - groupList.appendChild(memberList); - } - - // Helper function to update a label in IndexedDB. - private updateLabelForEmoji( - emojis: string, - newLabel: string, - db: IDBDatabase, - emojiSpan: HTMLElement, - processId: string - ) { - const transaction = db.transaction("labels", "readwrite"); - const store = transaction.objectStore("labels"); - const labelObject = { emoji: emojis, label: newLabel }; - const request = store.put(labelObject); - - request.onsuccess = () => { - emojiSpan.textContent = `${newLabel} : ${emojis}`; - this.reloadMemberChat(processId); - }; - } - - private async lookForDmProcess(): Promise { - const service = await Services.getInstance(); - const processes = await service.getMyProcesses(); - console.log(processes); - const recipientAddresses = await service.getAddressesForMemberId(this.selectedMember).sp_addresses; - console.log(recipientAddresses); - - for (const processId of processes) { - try { - const process = await service.getProcess(processId); - console.log(process); - const state = process.states[0]; // We assume that description never change and that we are part of the process from the beginning - const description = await service.decryptAttribute(processId, state, 'description'); - console.log(description); - if (!description || description !== "dm") { - continue; - } - const roles = service.getRoles(process); - if (!service.rolesContainsMember(roles, recipientAddresses)) { - console.error('Member is not part of the process'); - continue; - } - return processId; - } catch (e) { - console.error(e); - } - } - - return null; - } - - private async lookForMyDms(): Promise { - const service = await Services.getInstance(); - const processes = await service.getMyProcesses(); - const myAddresses = await service.getMemberFromDevice(); - const allMembers = await service.getAllMembers(); - - this.dmMembersSet.clear(); - - try { - for (const processId of processes) { - const process = await service.getProcess(processId); - const state = process.states[0]; - const description = await service.decryptAttribute(processId, state, 'description'); - if (!description || description !== "dm") { - continue; - } - const roles = service.getRoles(process); - const members = roles.dm.members; - for (const member of members) {; - if (!service.compareMembers(member.sp_addresses, myAddresses)) { - for (const [id, mem] of Object.entries(allMembers)) { - if (service.compareMembers(mem.sp_addresses, member.sp_addresses)) { - this.dmMembersSet.add(id); - break; - } - } - } - } - } - } catch (e) { - console.error(e); - } - console.log("dmMembersSet:", this.dmMembersSet); - return null; - } - - private async loadMemberChat(pairingProcess: string) { - if (this.isLoading) { - console.log('Already loading messages, skipping...'); - return; - } - - try { - this.isLoading = true; - const service = await Services.getInstance(); - const myAddresses = await service.getMemberFromDevice(); - const database = await Database.getInstance(); - const db = database.db; - - if (!myAddresses) { - console.error('No paired member found'); - return; - } - - // Set the selected member - this.selectedMember = pairingProcess; - console.log("SELECTED MEMBER: ", this.selectedMember); - - const chatHeader = this.shadowRoot?.querySelector('#chat-header'); - const messagesContainer = this.shadowRoot?.querySelector('#messages'); - - if (!chatHeader || !messagesContainer) return; - - messagesContainer.innerHTML = ''; - - const emojis = await addressToEmoji(pairingProcess); - - const transaction = db.transaction("labels", "readonly"); - const store = transaction.objectStore("labels"); - const request = store.get(emojis); - - request.onsuccess = () => { - const label = request.result; - chatHeader.textContent = label ? `Chat with ${label.label} (${emojis})` : `Chat with member (${emojis})`; - }; - - request.onerror = () => { - chatHeader.textContent = `Chat with member (${emojis})`; - }; - - let dmProcessId = await this.lookForDmProcess(); - - if (dmProcessId === null) { - console.log('Create a new dm process'); - // We need to create a new process - try { - const memberAddresses = await service.getAddressesForMemberId(this.selectedMember); - console.log("MEMBER ADDRESSES: ", memberAddresses); - // await service.checkConnections(otherMembers); - const res = await service.createDmProcess(memberAddresses.sp_addresses); - // We catch the new process here - const updatedProcess = res.updated_process?.current_process; - const processId = updatedProcess?.states[0]?.commited_in; - const stateId = updatedProcess?.states[0]?.state_id; - await service.handleApiReturn(res); - setTimeout(async () => { - // Now create a first commitment - console.log('Created a dm process', processId); - this.selectedChatProcessId = processId; - const createPrdReturn = await service.createPrdUpdate(processId, stateId); - await service.handleApiReturn(createPrdReturn); - const approveChangeReturn = await service.approveChange(processId, stateId); - await service.handleApiReturn(approveChangeReturn); - }, 500); - } catch (e) { - console.error(e); - return; - } - - while (dmProcessId === null) { - dmProcessId = await this.lookForDmProcess(); - await new Promise(r => setTimeout(r, 1000)); - } - } else { - console.log('Found DM process', dmProcessId); - this.selectedChatProcessId = dmProcessId; - } - - // Récupérer les messages depuis les états du processus - const allMessages: any[] = []; - - const dmProcess = await service.getProcess(this.selectedChatProcessId); - - console.log(dmProcess); - - if (dmProcess?.states) { - for (const state of dmProcess.states) { - if (state.state_id === '') { continue; } - const message = await service.decryptAttribute(this.selectedChatProcessId, state, 'message'); - if (message === "" || message === undefined || message === null) { - continue; - } - console.log('message', message); - allMessages.push(message); - } - } - - if (allMessages.length > 0) { - console.log('Messages found:', allMessages); - allMessages.sort((a, b) => a.metadata.createdAt - b.metadata.createdAt); - for (const message of allMessages) { - const messageElement = document.createElement('div'); - messageElement.className = 'message-container'; - - const myProcessId = await this.getMyProcessId(); - - const isCurrentUser = message.metadata.sender === myProcessId; - messageElement.style.justifyContent = isCurrentUser ? 'flex-end' : 'flex-start'; - - const messageContent = document.createElement('div'); - messageContent.className = isCurrentUser ? 'message user' : 'message'; - - const myEmoji = await addressToEmoji(myProcessId); - const otherEmoji = await addressToEmoji(this.selectedMember); - - const senderEmoji = isCurrentUser ? myEmoji : otherEmoji; - - if (message.type === 'file') { - let fileContent = ''; - if (message.content.type.startsWith('image/')) { - fileContent = ` -
    - Image -
    - `; - } else { - const blob = this.base64ToBlob(message.content.data, message.content.type); - const url = URL.createObjectURL(blob); - fileContent = ` - - `; - } - - messageContent.innerHTML = ` -
    - ${senderEmoji}: ${fileContent} -
    -
    - ${new Date(message.metadata.createdAt).toLocaleString('fr-FR')} -
    - `; - } else { - messageContent.innerHTML = ` -
    - ${senderEmoji}: ${message.content} -
    -
    - ${new Date(message.metadata.createdAt).toLocaleString('fr-FR')} -
    - `; - } - - messageElement.appendChild(messageContent); - messagesContainer.appendChild(messageElement); - } - - this.scrollToBottom(messagesContainer); - } else { - console.log('No messages found'); - } - this.scrollToBottom(messagesContainer); - } catch (error) { - console.error('❌ Error in loadMemberChat:', error); - } finally { - this.isLoading = false; - } - } - - private async reloadMemberChat(pairingProcess: string) { - try { - const service = await Services.getInstance(); - const database = await Database.getInstance(); - const db = database.db; - - const chatHeader = this.shadowRoot?.querySelector('#chat-header'); - const messagesContainer = this.shadowRoot?.querySelector('#messages'); - - if (!chatHeader || !messagesContainer) return; - - messagesContainer.innerHTML = ''; - - const emojis = await addressToEmoji(pairingProcess); - - const transaction = db.transaction("labels", "readonly"); - const store = transaction.objectStore("labels"); - const request = store.get(emojis); - - request.onsuccess = () => { - const label = request.result; - if (this.selectedMember === pairingProcess) { - chatHeader.textContent = label ? `Chat with ${label.label} (${emojis})` : `Chat with member (${emojis})`; - } - - }; - - request.onerror = () => { - chatHeader.textContent = `Chat with member (${emojis})`; - }; - - let dmProcessId = await this.selectedChatProcessId; - - // Récupérer les messages depuis les états du processus - const allMessages: any[] = []; - - const dmProcess = await service.getProcess(dmProcessId); - - console.log(dmProcess); - - if (dmProcess?.states) { - for (const state of dmProcess.states) { - if (!state.state_id) { continue; } - const message = await service.decryptAttribute(dmProcessId, state, 'message'); - if (message === "" || message === undefined || message === null) { - continue; - } - console.log('message', message); - allMessages.push(message); - } - } - - allMessages.sort((a, b) => a.metadata.createdAt - b.metadata.createdAt); - if (allMessages.length > 0) { - console.log('Messages found:', allMessages); - for (const message of allMessages) { - const messageElement = document.createElement('div'); - messageElement.className = 'message-container'; - - const myProcessId = await this.getMyProcessId(); - - const isCurrentUser = message.metadata.sender === myProcessId; - messageElement.style.justifyContent = isCurrentUser ? 'flex-end' : 'flex-start'; - - const messageContent = document.createElement('div'); - messageContent.className = isCurrentUser ? 'message user' : 'message'; - - - - const myEmoji = await addressToEmoji(myProcessId); - const otherEmoji = await addressToEmoji(this.selectedMember); - - const senderEmoji = isCurrentUser ? myEmoji : otherEmoji; - - if (message.type === 'file') { - let fileContent = ''; - if (message.content.type.startsWith('image/')) { - fileContent = ` -
    - Image -
    - `; - } else { - const blob = this.base64ToBlob(message.content.data, message.content.type); - const url = URL.createObjectURL(blob); - fileContent = ` - - `; - } - - messageContent.innerHTML = ` -
    - ${senderEmoji}: ${fileContent} -
    -
    - ${new Date(message.metadata.createdAt).toLocaleString('fr-FR')} -
    - `; - } else { - messageContent.innerHTML = ` -
    - ${senderEmoji}: ${message.content} -
    -
    - ${new Date(message.metadata.createdAt).toLocaleString('fr-FR')} -
    - `; - } - - messageElement.appendChild(messageContent); - messagesContainer.appendChild(messageElement); - } - - this.scrollToBottom(messagesContainer); - } else { - console.log('No messages found'); - } - this.scrollToBottom(messagesContainer); - } catch (error) { - console.error('❌ Error in reloadMemberChat:', error); - } - } - - private base64ToBlob(base64: string, type: string): Blob { - const byteCharacters = atob(base64.split(',')[1]); - const byteArrays = []; - - for (let offset = 0; offset < byteCharacters.length; offset += 512) { - const slice = byteCharacters.slice(offset, offset + 512); - const byteNumbers = new Array(slice.length); - - for (let i = 0; i < slice.length; i++) { - byteNumbers[i] = slice.charCodeAt(i); - } - - const byteArray = new Uint8Array(byteNumbers); - byteArrays.push(byteArray); - } - - return new Blob(byteArrays, { type: type }); - } - - //To get a map with key: sp address, value: pairing process - async getAddressMap() { - const service = await Services.getInstance(); - const allMembers = await service.getAllMembers(); - - const addressMap: Record = {}; - - for (const [key, values] of Object.entries(allMembers)) { - - if (values.sp_addresses) { - for (let value of values.sp_addresses) { - this.addressMap[value] = key; - } - } else { - console.log(`No sp_addresses array found for key "${key}"`); - } - } - return this.addressMap; - } - - async findProcessIdFromAddresses(addresses: string[]): Promise { - console.log('Addresses to find:', addresses); - const service = await Services.getInstance(); - const allMembers = await service.getAllMembers(); - console.log('Available members:', allMembers); - - const sortedAddresses = [...addresses].sort(); - - for (const [key, value] of Object.entries(allMembers)) { - if (value.sp_addresses.length === sortedAddresses.length) { - const sortedValue = [...value.sp_addresses].sort(); - if (sortedValue.every((val, index) => val === sortedAddresses [index])) { - return key; // Found a match - } - } - } - - return null; // No match found - } - - private async toggleMembers(roleData: any, roleElement: HTMLElement) { - console.log('Toggle members called with roleData:', roleData); - let memberList = roleElement.querySelector('.member-list'); - const roleName = roleElement.querySelector('.role-name')?.textContent || ''; - - if (memberList) { - console.log('Existing memberList found, toggling display'); - (memberList as HTMLElement).style.display = - (memberList as HTMLElement).style.display === 'none' ? 'block' : 'none'; - return; - } - - console.log('Creating new memberList'); - memberList = document.createElement('ul'); - memberList.className = 'member-list'; - - if (roleData.members) { - console.log('Members found:', roleData.members); - for (const member of roleData.members) { - console.log('Processing member:', member); - const memberItem = document.createElement('li'); - memberItem.className = 'member-item'; - - const memberContainer = document.createElement('div'); - memberContainer.className = 'member-container'; - - const emojiSpan = document.createElement('span'); - emojiSpan.className = 'member-emoji'; - - const pairingProcess = await this.findProcessIdFromAddresses(member.sp_addresses); - console.log('PairingProcess:', pairingProcess); - if (pairingProcess) { - //TO DO : faire apparaitre les membres avec lesquelels je suis pairé ? - const emojis = await addressToEmoji(pairingProcess); - console.log('Adresse pairée:', emojis); - emojiSpan.textContent = emojis; - } else { - const emojis = await addressToEmoji(member.sp_addresses[0]); - emojiSpan.textContent = emojis; - } - - memberContainer.appendChild(emojiSpan); - memberItem.appendChild(memberContainer); - - memberItem.onclick = async (event) => { - event.stopPropagation(); - try { - if (pairingProcess) { - await this.loadMemberChat(pairingProcess); - } - } catch (error) { - console.error('❌ Error handling member click:', error); - } - }; - - memberList.appendChild(memberItem); - } - } else { - console.log('No members found in roleData'); - } - - roleElement.appendChild(memberList); - } - - - private async switchTab(tabType: string, tabs: NodeListOf) { - const service = await Services.getInstance(); - - // Mettre à jour les classes des onglets - tabs.forEach(tab => { - tab.classList.toggle('active', tab.getAttribute('data-tab') === tabType); - }); - - // Supprimer le contenu existant sauf les onglets - const groupList = this.shadowRoot?.querySelector('#group-list'); - if (!groupList) return; - - const children = Array.from(groupList.children); - children.forEach(child => { - if (!child.classList.contains('tabs')) { - groupList.removeChild(child); - } - }); - - // Charger le contenu approprié - switch (tabType) { - case 'processes': - const processSet = await service.getMyProcesses(); - await this.loadAllProcesses(processSet); - break; - case 'members': - await this.lookForMyDms(); - await this.loadAllMembers(); - break; - default: - console.error('Unknown tab type:', tabType); - } - } - - //load all processes from the service - private async loadAllProcesses() { - console.log('🎯 Loading all processes'); - this.closeSignature(); - - const service = await Services.getInstance(); - const allProcesses: Record = await service.getProcesses(); - console.log('All processes:', allProcesses); - const myProcesses: string[] = await service.getMyProcesses(); - console.log('My processes:', myProcesses); - - const groupList = this.shadowRoot?.querySelector('#group-list'); - if (!groupList) { - console.warn('⚠️ Group list element not found'); - return; - } - - groupList.innerHTML = ''; - - const tabContent = document.createElement('div'); - tabContent.className = 'tabs'; - tabContent.innerHTML = ` - - - `; - groupList.appendChild(tabContent); - - // Ajouter les event listeners - const tabs = tabContent.querySelectorAll('.tab'); - tabs.forEach(tab => { - tab.addEventListener('click', () => { - const tabType = tab.getAttribute('data-tab'); - if (tabType) { - this.switchTab(tabType, tabs); - } - }); - }); - - // Trier les processus : ceux de l'utilisateur en premier - const sortedEntries = Object.entries(allProcesses).sort( - ([keyA], [keyB]) => { - const inSetA = myProcesses.includes(keyA); - const inSetB = myProcesses.includes(keyB); - return inSetB ? 1 : inSetA ? -1 : 0; - } - ); - - for (const [processId, process] of sortedEntries) { - // Create and configure the main list item. - const li = document.createElement('li'); - li.className = 'group-list-item'; - li.setAttribute('data-process-id', processId); - - // Retrieve roles for the current process. - const roles = service.getRoles(process); - if (!roles) { - console.error('Failed to get roles for process:', process); - continue; - } - - // If process is a pairing process, we don't want it in the list - if (service.isPairingProcess(roles)) { - continue; - } - - const publicData = service.getPublicData(process); - const processName = publicData['processName']; - const emoji = await addressToEmoji(processId); - - let displayName; - if (processName) { - displayName = `${processName} (${emoji})`; - } else { - displayName = `${defaultProcessName} (${emoji})`; - } - - // If the process is part of myProcesses, apply special styling. - if (myProcesses && myProcesses.includes(processId)) { - li.style.cssText = ` - background-color: var(--accent-color); - transition: background-color 0.3s ease; - cursor: pointer; - `; - li.addEventListener('mouseover', () => { - li.style.backgroundColor = 'var(--accent-color-hover)'; - }); - li.addEventListener('mouseout', () => { - li.style.backgroundColor = 'var(--accent-color)'; - }); - console.log("✅ Processus trouvé dans le set:", processId); - } - - // Attach a click handler for the process. - li.addEventListener('click', async (event) => { - event.stopPropagation(); - console.log("CLICKED ON PROCESS:", processId); - - // Update the signature header with the corresponding emoji. - const signatureHeader = this.shadowRoot?.querySelector('.signature-header h1'); - if (signatureHeader) { - if (processName) { - signatureHeader.textContent = `Signature of ${displayName}`; - } else { - signatureHeader.textContent = `Signature of ${displayName}`; - } - } - - this.openSignature(); - console.log('🎯 Roles de signature:', roles); - await this.loadAllRolesAndMembersInSignature(roles); - await this.newRequest(processId); - }); - - // Create the container for the process name and emoji. - const container = document.createElement('div'); - container.className = 'group-item-container'; - - // Create and set the process name element. - const nameSpan = document.createElement('span'); - nameSpan.className = 'process-name'; - nameSpan.textContent = displayName; - container.appendChild(nameSpan); - - li.appendChild(container); - - // Create a hidden list for roles. - const roleList = document.createElement('ul'); - roleList.className = 'role-list'; - roleList.style.display = 'none'; - - // Process each role and create role items. - Object.entries(roles).forEach(([roleName, roleData]) => { - const roleItem = document.createElement('li'); - roleItem.className = 'role-item'; - - const roleContainer = document.createElement('div'); - roleContainer.className = 'role-item-container'; - - const roleNameSpan = document.createElement('span'); - roleNameSpan.className = 'role-name'; - roleNameSpan.textContent = roleName; - - // Filter duplicate members by using the first sp_address as a key. - const uniqueMembers = new Map(); - roleData.members?.forEach(member => { - const spAddress = member.sp_addresses?.[0]; - if (spAddress && !uniqueMembers.has(spAddress)) { - uniqueMembers.set(spAddress, member); - } - }); - - // Create a new roleData object with unique members. - const filteredRoleData = { - ...roleData, - members: Array.from(uniqueMembers.values()), - }; - - // Attach a click handler for the role. - roleContainer.addEventListener('click', async (event) => { - event.stopPropagation(); - console.log("CLICKED ON ROLE:", roleName); - await this.toggleMembers(filteredRoleData, roleItem); - }); - - roleContainer.appendChild(roleNameSpan); - roleItem.appendChild(roleContainer); - roleList.appendChild(roleItem); - }); - - li.appendChild(roleList); - - // Toggle role list display when the container is clicked. - container.addEventListener('click', (event) => { - event.stopPropagation(); - container.classList.toggle('expanded'); - roleList.style.display = container.classList.contains('expanded') ? 'block' : 'none'; - }); - - // Append the completed process list item once. - groupList.appendChild(li); - } - - } - - private async newRequest(processId: string) { - const emoji = await addressToEmoji(processId); - const members = await this.getMembersFromProcess(processId); - const newRequestButton = this.shadowRoot?.querySelector('#request-document-button'); - if (newRequestButton) { - newRequestButton.replaceWith(newRequestButton.cloneNode(true)); - const freshButton = this.shadowRoot?.querySelector('#request-document-button'); - freshButton?.addEventListener('click', async () => { - const membersList = await this.generateMembersList(members); - - const modal = document.createElement('div'); - modal.className = 'request-modal'; - const today = new Date().toISOString().split('T')[0]; - - modal.innerHTML = ` - - `; - - this.shadowRoot?.appendChild(modal); - this.handleFileUpload(modal); - this.handleRequestButton(modal); - const closeButton = modal.querySelector('.close-modal'); - closeButton?.addEventListener('click', () => { - modal.remove(); - }); - }); - } - } - - //request button in the modal - private handleRequestButton(modal: HTMLElement) { - const requestButton = modal.querySelector('#send-request-button'); - requestButton?.addEventListener('click', () => { - console.log("REQUEST SENT"); - if (modal) { - //vérifier qu'au moins un membre est coché - const membersList = modal.querySelector('.members-list-modal'); - if (membersList) { - const members = membersList.querySelectorAll('.member-checkbox:checked'); - if (members.length === 0) { - alert('Please select at least one member'); - return; - } - } - //vérifier que la date est valide - const dateInput = modal.querySelector('#date-input') as HTMLInputElement; - if (dateInput) { - const date = new Date(dateInput.value); - if (isNaN(date.getTime())) { - alert('Please select a valid date'); - return; - } - } - - //verifier qu'un fichier a été load - const fileList = modal.querySelector('#file-list'); - if (fileList && fileList.children.length === 0) { - alert('Please upload at least one file'); - return; - } - - //récupérer le message - const messageInput = modal.querySelector('#message-input') as HTMLTextAreaElement; - if (messageInput) { - const message = messageInput.value; - } - //modal.remove(); - } - }); - } - - private handleFileUpload(modal: HTMLElement) { - const fileInput = modal.querySelector('#file-input') as HTMLInputElement; - const fileList = modal.querySelector('#file-list'); - const selectedFiles = new Set(); - - fileInput?.addEventListener('change', () => { - if (fileList && fileInput.files) { - Array.from(fileInput.files).forEach(file => { - if (!Array.from(selectedFiles).some(f => f.name === file.name)) { - selectedFiles.add(file); - const fileItem = document.createElement('div'); - fileItem.className = 'file-item'; - fileItem.innerHTML = ` - ${file.name} - - `; - fileList.appendChild(fileItem); - - fileItem.querySelector('.remove-file')?.addEventListener('click', () => { - selectedFiles.delete(file); - fileItem.remove(); - }); - } - }); - fileInput.value = ''; - } - }); - - return selectedFiles; - } - - private async generateMembersList(members: string[]) { - let html = ''; - for (const member of members) { - const emoji = await addressToEmoji(member); - html += `
  • ${emoji}
  • `; - } - return html; - } - - - //Send a set of members from a process - private async getMembersFromProcess(processId: string) { - const service = await Services.getInstance(); - const process = await service.getProcess(processId); - console.log("Process récupéré:", process); - - // Récupérer les rôles directement depuis le dernier état - const roles = service.getRoles(process); - console.log("Roles trouvés:", roles); - - if (!roles) return []; - type RoleData = { - members?: { sp_addresses?: string[] }[]; - }; - const uniqueMembers = new Set(); - Object.values(roles as unknown as Record).forEach((roleData: RoleData) => { - roleData.members?.forEach((member) => { - if (member.sp_addresses && member.sp_addresses[0]) { - uniqueMembers.add(member.sp_addresses[0]); - } - }); - }); - return Array.from(uniqueMembers); - } - - private async loadAllRolesAndMembersInSignature(roles: any) { - console.log('🎯 Roles:', roles); - const signatureDescription = this.shadowRoot?.querySelector('.signature-description'); - if (signatureDescription) { - signatureDescription.innerHTML = ''; - Object.entries(roles).forEach(([roleName, roleData]: [string, any]) => { - const roleItem = document.createElement('li'); - roleItem.className = 'role-signature'; - - const roleContainer = document.createElement('div'); - roleContainer.className = 'role-signature-container'; - - const roleNameSpan = document.createElement('span'); - roleNameSpan.className = 'role-signature-name'; - roleNameSpan.textContent = roleName; - - const uniqueMembers = new Map(); - roleData.members?.forEach((member: any) => { - const spAddress = member.sp_addresses?.[0]; - if (spAddress && !uniqueMembers.has(spAddress)) { - uniqueMembers.set(spAddress, member); - } - }); - - const filteredRoleData = { - ...roleData, - members: Array.from(uniqueMembers.values()) - }; - - roleContainer.addEventListener('click', async (event) => { - console.log("CLICKED ON ROLE:", roleName); - event.stopPropagation(); - await this.toggleMembers(filteredRoleData, roleItem); - }); - - roleContainer.appendChild(roleNameSpan); - roleItem.appendChild(roleContainer); - signatureDescription.appendChild(roleItem); - }); - } - } - - //fonction qui ferme la signature - private closeSignature() { - const closeSignature = this.shadowRoot?.querySelector('#close-signature'); - const signatureArea = this.shadowRoot?.querySelector('.signature-area'); - if (closeSignature && signatureArea) { - closeSignature.addEventListener('click', () => { - signatureArea.classList.add('hidden'); - }); - } - } - - //fonction qui ouvre la signature - private openSignature() { - const signatureArea = this.shadowRoot?.querySelector('.signature-area'); - if (signatureArea) { - signatureArea.classList.remove('hidden'); - } - } - - private async getMyProcessId() { - const service = await Services.getInstance(); - return service.getPairingProcessId(); - } - - //fonction qui renvoie les processus où le sp_adress est impliqué - private async getProcessesWhereTheCurrentMemberIs() { - const service = await Services.getInstance(); - try { - const currentMember = await service.getMemberFromDevice(); - if (!currentMember) { - console.error('❌ Pas de membre trouvé'); - return this.userProcessSet; - } - - const pairingProcess = await this.getMyProcessId(); - const memberEmoji = await addressToEmoji(pairingProcess); - console.log("Mon adresse:", currentMember[0], memberEmoji); - - const processes = await service.getProcesses(); - - for (const [processId, process] of Object.entries(processes)) { - try { - const roles = process.states[0]?.roles; - - if (!roles) { - console.log(`Pas de rôles trouvés pour le processus ${processId}`); - continue; - } - - for (const roleName in roles) { - const role = roles[roleName]; - - if (role.members && Array.isArray(role.members)) { - for (const member of role.members) { - if (member.sp_addresses && Array.isArray(member.sp_addresses)) { - if (member.sp_addresses.includes(currentMember[0])) { - this.userProcessSet.add(processId); - console.log(`Ajout du process ${processId} au Set (trouvé dans le rôle ${roleName})`); - break; - } - } - } - } - } - } catch (e) { - console.log(`Erreur lors du traitement du processus ${processId}:`, e); - continue; - } - } - - return this.userProcessSet; - } catch (e) { - console.error('❌ Erreur:', e); - return this.userProcessSet; - } - } - - // Send a file - private async sendFile(file: File) { - const MAX_FILE_SIZE = 1 * 1024 * 1024; - if (file.size > MAX_FILE_SIZE) { - alert('Le fichier est trop volumineux. Taille maximum : 1MB'); - return; - } - - try { - const service = await Services.getInstance(); - const myAddresses = await service.getMemberFromDevice(); - if (!myAddresses) throw new Error('No paired member found'); - - let fileData: string; - if (file.type.startsWith('image/')) { - fileData = await this.compressImage(file); - } else { - fileData = await this.readFileAsBase64(file); - } - - const timestamp = Date.now(); - const processId = this.getAttribute('process-id'); - const uniqueKey = `${processId}${timestamp}`; - - const dbRequest = indexedDB.open('4nk'); - - dbRequest.onerror = (event) => { - console.error("Database error:", dbRequest.error); - }; - - dbRequest.onsuccess = async (event) => { - const db = dbRequest.result; - const transaction = db.transaction(['diffs'], 'readwrite'); - const store = transaction.objectStore('diffs'); - - try { - // Message du fichier - const fileTemplate = { - value_commitment: uniqueKey, - messaging_id: processId, - description: 'message_content', - metadata: { - text: `Fichier envoyé: ${file.name}`, - timestamp: timestamp, - sender: myAddresses[0], - recipient: this.selectedMember, - messageState: this.messageState, - roleName: this.selectedRole, - type: 'file', - fileName: file.name, - fileType: file.type, - fileData: fileData - } - }; - - await new Promise((resolve, reject) => { - const request = store.add(fileTemplate); - request.onsuccess = () => { - console.log('✅ File message saved'); - resolve(); - }; - request.onerror = () => reject(request.error); - }); - - // Réponse automatique - const autoReplyTemplate = { - value_commitment: `${processId}${timestamp + 1000}`, - messaging_id: processId, - description: 'message_content', - metadata: { - text: "J'ai bien reçu votre fichier 📎", - timestamp: timestamp + 1000, - sender: this.selectedMember, - recipient: myAddresses[0], - messageState: this.messageState, - roleName: this.selectedRole - } - }; - - await new Promise((resolve, reject) => { - const request = store.add(autoReplyTemplate); - request.onsuccess = () => { - console.log('✅ Auto reply saved'); - if (myAddresses[0]) { - this.addNotification(myAddresses[0], autoReplyTemplate); - } - resolve(); - }; - request.onerror = () => reject(request.error); - }); - - // Attendre la fin de la transaction - await new Promise((resolve, reject) => { - transaction.oncomplete = () => { - console.log('✅ Transaction completed'); - resolve(); - }; - transaction.onerror = () => reject(transaction.error); - }); - - // Réinitialiser l'input file - const fileInput = this.shadowRoot?.querySelector('#file-input') as HTMLInputElement; - if (fileInput) fileInput.value = ''; - - // Recharger les messages - if (this.selectedMember) { - await this.loadMemberChat(this.selectedMember); - } - - } catch (error) { - console.error('❌ Transaction error:', error); - } - }; - - } catch (error) { - console.error('❌ Error in sendFile:', error); - } - } - - private async readFileAsBase64(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result as string); - reader.onerror = reject; - reader.readAsDataURL(file); - }); - } - - private async compressImage(file: File): Promise { - return new Promise((resolve, reject) => { - const img = new Image(); - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - - img.onload = () => { - let width = img.width; - let height = img.height; - const MAX_WIDTH = 800; - const MAX_HEIGHT = 600; - - if (width > height) { - if (width > MAX_WIDTH) { - height *= MAX_WIDTH / width; - width = MAX_WIDTH; - } - } else { - if (height > MAX_HEIGHT) { - width *= MAX_HEIGHT / height; - height = MAX_HEIGHT; - } - } - - canvas.width = width; - canvas.height = height; - ctx?.drawImage(img, 0, 0, width, height); - - resolve(canvas.toDataURL('image/jpeg', 0.7)); - }; - - img.onerror = reject; - img.src = URL.createObjectURL(file); - }); - } - - private async getProcesses(): Promise { - const service = await Services.getInstance(); - const processes = await service.getProcesses(); - - const res = Object.entries(processes).map(([key, value]) => ({ - key, - value, - })); - - return res; - } - - async connectedCallback() { - const service = await Services.getInstance(); - - const loadPage = async () => { - console.log("🔍 Chargement des processus par défaut"); - await this.loadAllProcesses(); - - if (this.selectedMember) { - console.log('🔍 Loading chat for selected member:', this.selectedMember); - await this.loadMemberChat(this.selectedMember); - } else { - console.warn('⚠️ No member selected yet. Waiting for selection...'); - } - } - - let timeout: NodeJS.Timeout; - window.addEventListener('process-updated', async (e: CustomEvent) => { - const processId = e.detail.processId; - console.log('Notified of an update for process', processId); - await loadPage(); - }); - - await loadPage(); - } -} - -customElements.define('chat-element', ChatElement); -export { ChatElement };*/ - diff --git a/src/pages/process-element/process-component.ts b/src/pages/process-element/process-component.ts deleted file mode 100644 index 31ba6e6..0000000 --- a/src/pages/process-element/process-component.ts +++ /dev/null @@ -1,51 +0,0 @@ -import processHtml from './process-element.html?raw'; -import processScript from './process-element.ts?raw'; -import processCss from '../../4nk.css?raw'; -import { initProcessElement } from './process-element'; - -export class ProcessListComponent extends HTMLElement { - _callback: any; - id: string = ''; - zone: string = ''; - - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - } - - connectedCallback() { - console.log('CALLBACK PROCESS LIST PAGE'); - this.render(); - setTimeout(() => { - initProcessElement(this.id, this.zone); - }, 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 = ` - ${processHtml} - - - - diff --git a/src/pages/signature/signature.ts b/src/pages/signature/signature.ts deleted file mode 100755 index f5b60fa..0000000 --- a/src/pages/signature/signature.ts +++ /dev/null @@ -1,1758 +0,0 @@ -import signatureStyle from '../../../public/style/signature.css?inline'; - -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 { 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 showAlert(message: string): void { - // Créer la popup si elle n'existe pas - let alertPopup = this.shadowRoot?.querySelector('.alert-popup'); - if (!alertPopup) { - alertPopup = document.createElement('div'); - alertPopup.className = 'alert-popup'; - this.shadowRoot?.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); - } - - private signDocument(documentId: number, processId: number, isCommonDocument: boolean = false): void { - try { - if (typeof window === 'undefined' || typeof document === 'undefined') { - console.error('Cette fonction ne peut être exécutée que dans un navigateur'); - return; - } - - const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock)); - const group = groups.find((g: 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) { - this.showAlert("You do not have the necessary rights to sign this document."); - return; - } - - // Create and insert the modal directly into the body - const modalHtml = ` - `; - - - const modalElement = document.createElement('div'); - modalElement.className = 'modal-overlay'; - modalElement.innerHTML = modalHtml; - this.shadowRoot?.appendChild(modalElement); - - - const slider = modalElement.querySelector('#signatureSlider'); - const sliderTrack = slider?.parentElement; - let isDragging = false; - let startX: number; - let sliderLeft: number; - - if (slider && sliderTrack) { - slider.addEventListener('mousedown', (e: Event) => initDrag(e as MouseEvent)); - slider.addEventListener('touchstart', (e: Event) => initDrag(e as TouchEvent)); - - modalElement.addEventListener('mousemove', (e: Event) => drag.call(this, e as MouseEvent)); - modalElement.addEventListener('touchmove', (e: Event) => drag.call(this, e as TouchEvent)); - modalElement.addEventListener('mouseup', (e: Event) => stopDrag(e as MouseEvent)); - modalElement.addEventListener('touchend', (e: Event) => stopDrag(e as TouchEvent)); - } - - function initDrag(e: MouseEvent | TouchEvent) { - isDragging = true; - startX = 'touches' in e ? e.touches[0].clientX : e.clientX; - sliderLeft = (slider as HTMLElement)?.offsetLeft || 0; - } - - function drag(this: SignatureElement, e: MouseEvent | TouchEvent) { - if (!isDragging || !slider || !sliderTrack) return; - - e.preventDefault(); - const x = 'touches' in e ? e.touches[0].clientX : e.clientX; - const deltaX = x - startX; - - // Calculate the position relative to the track - let newLeft = sliderLeft + deltaX; - - // Limit the movement - const maxLeft = (sliderTrack as HTMLElement).offsetWidth - (slider as HTMLElement).offsetWidth; - newLeft = Math.max(0, Math.min(newLeft, maxLeft)); - - // Update the position - (slider as HTMLElement).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 as HTMLElement).offsetLeft < ((sliderTrack as HTMLElement)?.offsetWidth || 0) * 0.9) { - (slider as HTMLElement).style.left = '0px'; - } - } - - } catch (error) { - console.error('Error displaying modal:', error); - this.showAlert(error instanceof Error ? error.message : 'Error displaying modal'); - } - } - - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - this.dom = getCorrectDOM('signature-element'); - - this.shadowRoot!.innerHTML = ` - -
    - -
    -
      -
    -
    - - -
    -
    - -
    -
    - -
    - - -
    - - - - -
    -
    -
    - `; - - 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 = this.shadowRoot?.getElementById('send-button'); - if (sendButton) { - sendButton.addEventListener('click', () => this.sendMessage()); - } - - // Pour la touche Entrée - const messageInput = this.shadowRoot?.getElementById('message-input'); - if (messageInput) { - messageInput.addEventListener('keypress', (event: KeyboardEvent) => { - if (event.key === 'Enter' && !event.shiftKey) { - event.preventDefault(); - this.sendMessage(); - } - }); - } - } - - private initFileUpload() { - const fileInput = this.shadowRoot?.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 = this.shadowRoot?.getElementById(`process-details-${groupId}`); - const chatArea = this.shadowRoot?.getElementById('chat-area'); - - if (detailsArea) { - detailsArea.style.display = 'none'; - } - - if (chatArea) { - chatArea.style.display = 'block'; - } - } - - ///////////////////// Notification module ///////////////////// - // Delete a notification - 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 = this.shadowRoot?.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 = this.shadowRoot?.querySelectorAll('.process-details'); - if (allDetailsAreas) { - allDetailsAreas.forEach(area => { - (area as HTMLElement).style.display = 'none'; - }); - } - - const container = this.shadowRoot?.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 = this.shadowRoot?.getElementById(`process-details-${groupId}`); - if (!detailsArea) { - detailsArea = document.createElement('div'); - detailsArea.id = `process-details-${groupId}`; - detailsArea.className = 'process-details'; - container.appendChild(detailsArea); - } - - if (detailsArea) { - detailsArea.style.display = 'block'; - detailsArea.innerHTML = ` -
    -

    ${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 - Waiting for creation

    '} -
    - ${!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 = this.shadowRoot?.getElementById('chat-header'); - const messagesContainer = this.shadowRoot?.getElementById('messages'); - - if (!chatHeader || !messagesContainer) return; - - chatHeader.textContent = `Chat with ${memberInfo.roleName} ${memberInfo.memberName} from ${memberInfo.processName}`; - messagesContainer.innerHTML = ''; - - if (memberMessages) { - memberMessages.messages.forEach((message: 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 = this.shadowRoot?.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 = this.shadowRoot?.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 = this.shadowRoot?.querySelectorAll('.process-details'); - allDetailsAreas?.forEach(area => { - area.remove(); - }); - - const container = this.shadowRoot?.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 = this.shadowRoot?.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); - this.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); - this.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); - this.showAlert('Error retrieving members'); - return; - } - - - - modal.innerHTML = ` - - `; - - this.shadowRoot?.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 = this.shadowRoot?.getElementById('newDocumentForm') as HTMLFormElement; - if (!form) { - this.showAlert('Form not found'); - return; - } - - // Retrieve the files - const fileList = this.shadowRoot?.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) { - this.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) { - this.showAlert('Process not found'); - return; - } - - const role = group.roles.find((r: any) => - r.documents?.some((d: any) => d.id === documentId) - ); - - if (!role) { - this.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); - this.showAlert('Document updated successfully!'); - - } catch (error) { - console.error('Error saving:', error); - this.showAlert('An error occurred while saving'); - } - } - - private submitCommonDocument(event: Event) { - event.preventDefault(); - - const form = this.shadowRoot?.getElementById('newDocumentForm') as HTMLFormElement; - if (!form) { - this.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) { - this.showAlert('Please fill in all required fields'); - return; - } - - try { - const groups = JSON.parse(localStorage.getItem('groups') || JSON.stringify(groupsMock)); - const group = groups.find((g: Group) => g.id === processId); - - if (!group) { - this.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 = this.shadowRoot?.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); - this.showAlert('Document common updated successfully!'); - - } catch (error) { - console.error('Error saving:', error); - this.showAlert('An error occurred while saving'); - } - } - - - private submitRequest() { - - this.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 = (this.shadowRoot?.getElementById('createdAt') as HTMLInputElement)?.value || ''; - const deadline = (this.shadowRoot?.getElementById('deadline') as HTMLInputElement)?.value || ''; - const visibility = (this.shadowRoot?.getElementById('visibility') as HTMLSelectElement)?.value || ''; - const description = (this.shadowRoot?.getElementById('description') as HTMLTextAreaElement)?.value || ''; - - const selectedMembers = Array.from( - this.shadowRoot?.querySelectorAll('input[name="selected-members"]:checked') || [] - ).map(checkbox => (checkbox as HTMLInputElement).value); - - if (!createdAt || !deadline || selectedMembers.length === 0) { - this.showAlert('Please fill in all required fields and select at least one member.'); - return; - } - - console.log('Document submission:', { - documentId, - createdAt, - deadline, - visibility, - description, - selectedMembers - }); - - this.showAlert('Document request submitted successfully!'); - this.closeNewRequest(); - } - - // FUNCTIONS FOR SIGNATURE - - // New function to confirm the signature - 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.`); - } - - // Mettre à jour la signature - userSignature.signed = true; - userSignature.signedAt = new Date().toISOString(); - localStorage.setItem('groups', JSON.stringify(groups)); - - // Supprimer la modal de signature - const modalOverlay = this.shadowRoot?.querySelector('.modal-overlay'); - if (modalOverlay) { - modalOverlay.remove(); - } - - // Rafraîchir l'affichage - if (isCommonDocument) { - this.showProcessDetails(group, processId); - } else { - const role = group.roles.find((r: any) => r.documents?.includes(targetDoc)); - if (role) { - this.showRoleDocuments(role, group); - } - } - - this.showAlert('Document signed successfully!'); - - } catch (error) { - console.error('Error signing document:', error); - this.showAlert(error instanceof Error ? error.message : 'Error signing document'); - } - } - - - private initializeEventListeners() { - document.addEventListener('DOMContentLoaded', (): void => { - const newRequestBtn = this.shadowRoot?.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() { - 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 6e7d965..7b1620f 100755 --- a/src/router.ts +++ b/src/router.ts @@ -83,11 +83,8 @@ async function handleLocation(path: string) { break; 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]); - } + // Process element functionality removed + console.warn('Process element functionality has been removed'); break; case 'account': @@ -107,15 +104,8 @@ async function handleLocation(path: string) { 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); - } + // Signature functionality removed + console.warn('Signature functionality has been removed'); break; } } @@ -936,7 +926,7 @@ export async function registerAllListeners() { await handleCreateProcess(event); break; case MessageType.CREATE_CONVERSATION: - await handleCreateConversationProcess(event); + console.warn('CREATE_CONVERSATION functionality has been removed'); break; case MessageType.NOTIFY_UPDATE: await handleNotifyUpdate(event); diff --git a/src/services/database.service.ts b/src/services/database.service.ts index 665ca0c..23f64a5 100755 --- a/src/services/database.service.ts +++ b/src/services/database.service.ts @@ -203,7 +203,7 @@ export class Database { resolve(registration.active); } }; - + navigator.serviceWorker.addEventListener('controllerchange', listener); }); } diff --git a/src/services/service.ts b/src/services/service.ts index a9d9655..d16b7ab 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -893,7 +893,7 @@ export default class Services { const currentPairingId = this.sdkClient.get_pairing_process_id(); console.log(`🔍 Current pairing process ID from SDK: ${currentPairingId}`); } catch (e) { - console.log(`⚠️ SDK pairing process ID not available yet: ${e.message}`); + console.log(`⚠️ SDK pairing process ID not available yet: ${(e as Error).message}`); } // Try to force synchronization by requesting the process from peers @@ -903,24 +903,24 @@ export default class Services { await this.requestDataFromPeers(processId, [], []); console.log(`✅ Process request sent to peers`); } catch (e) { - console.log(`⚠️ Failed to request process from peers: ${e.message}`); + console.log(`⚠️ Failed to request process from peers: ${(e as Error).message}`); } } // Check if the process exists in our processes list try { - const process = this.getProcess(processId); + const process = await this.getProcess(processId); if (process) { console.log(`🔍 Process exists: ${processId}, states: ${process.states?.length || 0}`); const lastState = process.states?.[process.states.length - 1]; if (lastState) { - console.log(`🔍 Last state ID: ${lastState.state_id}, committed: ${lastState.committed}`); + console.log(`🔍 Last state ID: ${lastState.state_id}`); } } else { console.log(`⚠️ Process not found in local processes: ${processId}`); } } catch (e) { - console.log(`⚠️ Error checking process: ${e.message}`); + console.log(`⚠️ Error checking process: ${(e as Error).message}`); } // Check WebSocket connection and handshake data @@ -929,7 +929,7 @@ export default class Services { console.log(`🔍 Current block height: ${this.currentBlockHeight}`); console.log(`🔍 Members list size: ${Object.keys(this.membersList).length}`); } catch (e) { - console.log(`⚠️ Error checking WebSocket state: ${e.message}`); + console.log(`⚠️ Error checking WebSocket state: ${(e as Error).message}`); } // Check if the commitment is set and not null/empty @@ -948,12 +948,12 @@ export default class Services { await this.updateDevice(); console.log(`✅ Device update successful on attempt ${i + 1}`); } catch (e) { - console.log(`⚠️ Device update failed on attempt ${i + 1} (process may not be committed yet): ${e.message}`); + console.log(`⚠️ Device update failed on attempt ${i + 1} (process may not be committed yet): ${(e as Error).message}`); } } - } catch (e) { - console.log(`❌ Attempt ${i + 1}/${maxRetries}: Error during synchronization - ${e.message}`); - } + } catch (e) { + console.log(`❌ Attempt ${i + 1}/${maxRetries}: Error during synchronization - ${(e as Error).message}`); + } if (i < maxRetries - 1) { console.log(`⏳ Waiting ${retryDelay}ms before next attempt...`); diff --git a/src/utils/messageMock.ts b/src/utils/messageMock.ts deleted file mode 100755 index cae381f..0000000 --- a/src/utils/messageMock.ts +++ /dev/null @@ -1,53 +0,0 @@ -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 88c5caf..7aa07a6 100755 --- a/src/utils/notification.store.ts +++ b/src/utils/notification.store.ts @@ -79,9 +79,8 @@ class NotificationStore { ${notif.time ? `
    ${notif.time}
    ` : ''} `; notifElement.onclick = () => { - if (notif.memberId) { - window.loadMemberChat(notif.memberId); - } + // Chat functionality removed + console.warn('Chat functionality has been removed'); this.removeNotification(index); }; board.appendChild(notifElement); diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts index c5f15e5..3b55f74 100755 --- a/src/utils/sp-address.utils.ts +++ b/src/utils/sp-address.utils.ts @@ -423,15 +423,15 @@ function showLoadingState() { const joinerFlow = container.querySelector('#joiner-flow'); if (loadingFlow) { - loadingFlow.style.display = 'block'; + (loadingFlow as HTMLElement).style.display = 'block'; // Update loading message const loadingText = loadingFlow.querySelector('h2'); const loadingDesc = loadingFlow.querySelector('p'); if (loadingText) loadingText.textContent = 'Initializing...'; if (loadingDesc) loadingDesc.textContent = 'Setting up secure pairing interface'; } - if (creatorFlow) creatorFlow.style.display = 'none'; - if (joinerFlow) joinerFlow.style.display = 'none'; + if (creatorFlow) (creatorFlow as HTMLElement).style.display = 'none'; + if (joinerFlow) (joinerFlow as HTMLElement).style.display = 'none'; } // Detect flow and show appropriate interface @@ -447,16 +447,16 @@ async function detectAndShowFlow() { if (hasWords) { // Joiner flow - if (loadingFlow) loadingFlow.style.display = 'none'; - if (creatorFlow) creatorFlow.style.display = 'none'; - if (joinerFlow) joinerFlow.style.display = 'block'; + if (loadingFlow) (loadingFlow as HTMLElement).style.display = 'none'; + if (creatorFlow) (creatorFlow as HTMLElement).style.display = 'none'; + if (joinerFlow) (joinerFlow as HTMLElement).style.display = 'block'; updateJoinerStatus('Ready to join pairing'); } else { // Creator flow - if (loadingFlow) loadingFlow.style.display = 'none'; - if (creatorFlow) creatorFlow.style.display = 'block'; - if (joinerFlow) joinerFlow.style.display = 'none'; + if (loadingFlow) (loadingFlow as HTMLElement).style.display = 'none'; + if (creatorFlow) (creatorFlow as HTMLElement).style.display = 'block'; + if (joinerFlow) (joinerFlow as HTMLElement).style.display = 'none'; updateCreatorStatus('Ready to create pairing'); } @@ -596,7 +596,7 @@ async function onCreateButtonClick() { // Wait for pairing commitment with synchronization updateJoinerStatus('Synchronizing with network...'); console.log("🔍 DEBUG: Joiner - About to call waitForPairingCommitment..."); - await service.waitForPairingCommitment(pairingId); + await service.waitForPairingCommitment(pairingId!); console.log("✅ DEBUG: Joiner - waitForPairingCommitment completed!"); // Then confirm pairing @@ -625,7 +625,7 @@ async function onCreateButtonClick() { // Wait for pairing commitment with synchronization updateCreatorStatus('Synchronizing with network...'); console.log("🔍 DEBUG: Creator - About to call waitForPairingCommitment..."); - await service.waitForPairingCommitment(pairingId); + await service.waitForPairingCommitment(pairingId!); console.log("✅ DEBUG: Creator - waitForPairingCommitment completed!"); // Then confirm pairing @@ -642,7 +642,7 @@ async function onCreateButtonClick() { }, 2000); } } catch (e) { - console.error(`onCreateButtonClick error: ${e}`); + console.error(`onCreateButtonClick error: ${(e as Error).message}`); } } @@ -690,7 +690,7 @@ export async function discoverAndJoinPairingProcessWithWords(words: string): Pro console.log(`⏳ Still waiting for pairing process... (${i + 1}/${maxRetries})`); } catch (e) { - console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${e.message}`); + console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${(e as Error).message}`); } if (i < maxRetries - 1) { @@ -701,7 +701,7 @@ export async function discoverAndJoinPairingProcessWithWords(words: string): Pro throw new Error(`❌ Failed to discover pairing process after ${maxRetries} attempts`); } catch (err) { - console.error(`❌ Joiner discovery failed:`, err); + console.error(`❌ Joiner discovery failed:`, (err as Error).message); throw err; } } @@ -750,7 +750,7 @@ export async function discoverAndJoinPairingProcess(creatorAddress: string): Pro console.log(`⏳ Still waiting for pairing process... (${i + 1}/${maxRetries})`); } catch (e) { - console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${e.message}`); + console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${(e as Error).message}`); } if (i < maxRetries - 1) { @@ -761,7 +761,7 @@ export async function discoverAndJoinPairingProcess(creatorAddress: string): Pro throw new Error(`❌ Failed to discover pairing process after ${maxRetries} attempts`); } catch (err) { - console.error(`❌ Joiner discovery failed:`, err); + console.error(`❌ Joiner discovery failed:`, (err as Error).message); throw err; } } @@ -849,7 +849,7 @@ export async function waitForJoinerAndUpdateProcess(): Promise { console.log(`⏳ Still waiting for joiner... (${i + 1}/${maxRetries})`); } catch (e) { - console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${e.message}`); + console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${(e as Error).message}`); } if (i < maxRetries - 1) {