declare global { interface Window { initAccount: () => void; showContractPopup: (contractId: string) => void; showPairing: () => Promise; showWallet: () => void; showData: () => void; addWalletRow: () => void; confirmWalletRow: () => void; cancelWalletRow: () => void; openAvatarPopup: () => void; closeAvatarPopup: () => void; editDeviceName: (cell: HTMLTableCellElement) => void; showNotifications: (processName: string) => void; closeNotificationPopup: (event: Event) => void; markAsRead: (processName: string, messageId: number, element: HTMLElement) => void; exportRecovery: () => void; confirmDeleteAccount: () => void; deleteAccount: () => void; updateNavbarBanner: (bannerUrl: string) => void; saveBannerToLocalStorage: (bannerUrl: string) => void; loadSavedBanner: () => void; cancelAddRowPairing: () => void; saveName: (cell: HTMLElement, input: HTMLInputElement) => void; showProcessNotifications: (processName: string) => void; handleLogout: () => void; initializeEventListeners: () => void; showProcess: () => void; showProcessCreation: () => void; showDocumentValidation: () => void; updateNavbarName: (name: string) => void; updateNavbarLastName: (lastName: string) => void; showAlert: (title: string, text?: string, icon?: string) => void; addRowPairing: () => void; confirmRowPairing: () => void; cancelRowPairing: () => void; deleteRowPairing: (button: HTMLButtonElement) => void; generateRecoveryWords: () => string[]; exportUserData: () => void; updateActionButtons: () => void; showQRCodeModal: (pairingId: string) => void; } } import Swal from 'sweetalert2'; import { STORAGE_KEYS, defaultRows, mockProcessRows, mockNotifications, notificationMessages, mockDataRows, mockContracts, ALLOWED_ROLES } from '../../mocks/mock-account/constAccountMock'; import { Row, WalletRow, DataRow, Notification, Contract, NotificationMessage } from '../../mocks/mock-account/interfacesAccountMock'; import { addressToEmoji } from '../../utils/sp-address.utils'; import { getCorrectDOM } from '../../utils/document.utils'; import accountStyle from '../../../public/style/account.css?inline'; import Services from '../../services/service'; import { getProcessCreation } from './process-creation'; import { getDocumentValidation } from './document-validation'; import { createProcessTab } from './process'; let isAddingRow = false; let currentRow: HTMLTableRowElement | null = null; let currentMode: keyof typeof STORAGE_KEYS = 'pairing'; interface Process { states: Array<{ committed_in: string; keys: {}; pcd_commitment: { counter: string; }; public_data: { memberPublicName?: string; }; roles: { pairing?: {}; }; state_id: string; validation_tokens: Array; }>; } class AccountElement extends HTMLElement { private dom: Node; constructor() { super(); this.attachShadow({ mode: 'open' }); this.dom = getCorrectDOM('account-element'); // Ajouter Font Awesome const fontAwesome = document.createElement('link'); fontAwesome.rel = 'stylesheet'; fontAwesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css'; this.shadowRoot!.appendChild(fontAwesome); const style = document.createElement('style'); style.textContent = accountStyle; this.shadowRoot!.appendChild(style); this.shadowRoot!.innerHTML = `
    Banner
    Avatar
    Pairing 🔗
    Process ⚙️
    Process Creation
    Document Validation
`; window.showPairing = () => this.showPairing(); window.showWallet = () => this.showWallet(); window.showProcess = () => this.showProcess(); window.showProcessCreation = () => this.showProcessCreation(); window.showDocumentValidation = () => this.showDocumentValidation(); window.showData = () => this.showData(); window.addWalletRow = () => this.addWalletRow(); window.confirmWalletRow = () => this.confirmWalletRow(); window.cancelWalletRow = () => this.cancelWalletRow(); window.editDeviceName = (cell: HTMLTableCellElement) => this.editDeviceName(cell); window.showProcessNotifications = (processName: string) => this.showProcessNotifications(processName); window.handleLogout = () => this.handleLogout(); window.confirmDeleteAccount = () => this.confirmDeleteAccount(); window.showContractPopup = (contractId: string) => this.showContractPopup(contractId); window.addRowPairing = () => this.addRowPairing(); window.deleteRowPairing = (button: HTMLButtonElement) => this.deleteRowPairing(button); window.confirmRowPairing = () => this.confirmRowPairing(); window.cancelRowPairing = () => this.cancelRowPairing(); window.updateNavbarBanner = (bannerUrl: string) => this.updateNavbarBanner(bannerUrl); window.saveBannerToLocalStorage = (bannerUrl: string) => this.saveBannerToLocalStorage(bannerUrl); window.loadSavedBanner = () => this.loadSavedBanner(); window.closeNotificationPopup = (event: Event) => this.closeNotificationPopup(event); window.markAsRead = (processName: string, messageId: number, element: HTMLElement) => this.markAsRead(processName, messageId, element); window.exportRecovery = () => this.exportRecovery(); window.generateRecoveryWords = () => this.generateRecoveryWords(); window.exportUserData = () => this.exportUserData(); window.updateActionButtons = () => this.updateActionButtons(); window.openAvatarPopup = () => this.openAvatarPopup(); window.closeAvatarPopup = () => this.closeAvatarPopup(); window.showQRCodeModal = (pairingId: string) => this.showQRCodeModal(pairingId); if (!localStorage.getItem('rows')) { localStorage.setItem('rows', JSON.stringify(defaultRows)); } } connectedCallback() { this.initializeEventListeners(); this.loadSavedBanner(); this.loadUserInfo(); const savedAvatar = localStorage.getItem('userAvatar'); const savedBanner = localStorage.getItem('userBanner'); const savedName = localStorage.getItem('userName'); const savedLastName = localStorage.getItem('userLastName'); if (savedAvatar) { const navAvatar = this.shadowRoot?.querySelector('.avatar') as HTMLImageElement; if (navAvatar) navAvatar.src = savedAvatar; } if (savedBanner) { const navBanner = this.shadowRoot?.querySelector('.banner-image') as HTMLImageElement; if (navBanner) navBanner.src = savedBanner; } if (savedName) { this.updateNavbarName(savedName); } if (savedLastName) { this.updateNavbarLastName(savedLastName); } } 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); } // Fonctions de gestion des comptes et de l'interface utilisateur private confirmDeleteAccount(): void { const modal = document.createElement('div'); modal.className = 'confirm-delete-modal'; modal.innerHTML = `

Delete Account

Are you sure you want to delete your account? This action cannot be undone.

`; this.shadowRoot?.appendChild(modal); modal.style.display = 'block'; const cancelBtn = modal.querySelector('.cancel-btn'); const confirmBtn = modal.querySelector('.confirm-btn'); cancelBtn?.addEventListener('click', () => { modal.remove(); }); confirmBtn?.addEventListener('click', () => { this.deleteAccount(); modal.remove(); }); } private deleteAccount(): void { localStorage.clear(); window.location.href = '/login.html'; } private updateNavbarBanner(imageUrl: string): void { const navbarSection = this.shadowRoot?.querySelector('.nav-wrapper .avatar-section'); if (!navbarSection) return; let bannerImg = navbarSection.querySelector('.banner-image'); if (!bannerImg) { bannerImg = document.createElement('img'); bannerImg.className = 'banner-image'; navbarSection.insertBefore(bannerImg, navbarSection.firstChild); } bannerImg.src = imageUrl; } private saveBannerToLocalStorage(dataUrl: string): void { localStorage.setItem('userBanner', dataUrl); } private loadSavedBanner(): void { const savedBanner = localStorage.getItem('userBanner'); if (savedBanner) { const bannerImg = this.shadowRoot?.getElementById('popup-banner-img') as HTMLImageElement; if (bannerImg) { bannerImg.src = savedBanner; } this.updateNavbarBanner(savedBanner); } } private closeNotificationPopup(event: Event): void { const target = event.target as HTMLElement; const isOverlay = target.classList.contains('notification-popup-overlay'); const isCloseButton = target.classList.contains('close-popup'); if (!isOverlay && !isCloseButton) return; const popup = this.shadowRoot?.querySelector('.notification-popup-overlay'); if (popup) popup.remove(); } private markAsRead(processName: string, messageId: number, element: HTMLElement): void { const process = mockProcessRows.find(p => p.process === processName); if (!process) return; const message = process.notification.messages.find(m => m.id === messageId); if (!message || message.read) return; message.read = true; element.classList.remove('unread'); element.classList.add('read'); const statusIcon = element.querySelector('.notification-status'); if (statusIcon) { statusIcon.innerHTML = ` `; } const notifCount = this.calculateNotifications(process.notification.messages); const countElement = this.shadowRoot?.querySelector(`.notification-count[data-process="${processName}"]`); if (countElement) { countElement.textContent = `${notifCount.unread}/${notifCount.total}`; const bellContainer = countElement.closest('.notification-container'); const bell = bellContainer?.querySelector('svg'); // Changé de .fa-bell à svg if (bell && bellContainer && notifCount.unread === 0) { bellContainer.classList.remove('has-unread'); (bell as SVGElement).style.fill = '#666'; // Utiliser fill au lieu de color pour SVG } } } // Fonctions de gestion des données et de l'interface private calculateNotifications(messages: NotificationMessage[]): { unread: number; total: number } { const total = messages.length; const unread = messages.filter(msg => !msg.read).length; return { unread, total }; } // Fonctions de récupération private exportRecovery(): void { Swal.fire({ title: 'Recovery Words Export', text: '4 words will be displayed. We strongly recommend writing them down on paper before exporting the account. Do you want to continue?', icon: 'warning', showCancelButton: true, confirmButtonText: 'Confirm', cancelButtonText: 'Cancel', confirmButtonColor: '#C89666', cancelButtonColor: '#6c757d', // Ajouter des styles personnalisés customClass: { container: 'recovery-popup-container', popup: 'recovery-popup' } }).then((result) => { if (result.isConfirmed) { const recoveryWords = this.generateRecoveryWords(); localStorage.setItem('recoveryWords', JSON.stringify(recoveryWords)); Swal.fire({ title: 'Your Recovery Words', html: `
${recoveryWords.map((word, index) => `
${index + 1}. ${word}
`).join('')}
Please write these words down carefully. They will be needed to recover your account.
`, showCancelButton: false, confirmButtonText: 'I confirm the export', confirmButtonColor: '#C89666', allowOutsideClick: false, allowEscapeKey: false, customClass: { container: 'recovery-popup-container', popup: 'recovery-popup' } }).then((result) => { if (result.isConfirmed) { // Stocker l'état du bouton dans le localStorage localStorage.setItem('recoveryExported', 'true'); const exportRecoveryBtn = this.shadowRoot?.querySelector('.recovery-btn') as HTMLButtonElement; if (exportRecoveryBtn) { exportRecoveryBtn.disabled = true; exportRecoveryBtn.style.opacity = '0.5'; exportRecoveryBtn.style.cursor = 'not-allowed'; } } }); } }); } private generateRecoveryWords(): string[] { const wordsList = [ 'apple', 'banana', 'orange', 'grape', 'kiwi', 'mango', 'peach', 'plum', 'lemon', 'lime', 'cherry', 'melon', 'pear', 'fig', 'date', 'berry' ]; const recoveryWords: string[] = []; while (recoveryWords.length < 4) { const randomWord = wordsList[Math.floor(Math.random() * wordsList.length)]; if (!recoveryWords.includes(randomWord)) { recoveryWords.push(randomWord); } } return recoveryWords; } private exportUserData(): void { const data: { [key: string]: string | null } = {}; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key) { const value = localStorage.getItem(key); data[key] = value; } } const jsonData = JSON.stringify(data, null, 2); const blob = new Blob([jsonData], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'user_data.json'; this.shadowRoot?.appendChild(a); a.click(); this.shadowRoot?.removeChild(a); URL.revokeObjectURL(url); } private updateActionButtons(): void { const buttonContainer = this.shadowRoot?.querySelector('.button-container'); if (!buttonContainer) return; buttonContainer.innerHTML = `
`; } private getConfirmFunction(): string { switch (currentMode) { case 'wallet': return 'window.confirmWalletRow()'; case 'process': return 'window.confirmProcessRow()'; default: return 'window.confirmRowPairing()'; } } private getCancelFunction(): string { switch (currentMode) { case 'wallet': return 'window.cancelWalletRow()'; case 'process': return 'window.cancelProcessRow()'; default: return 'window.cancelRowPairing()'; } } // Fonctions de gestion des tableaux private async addRowPairing(): Promise { if (isAddingRow) return; isAddingRow = true; // Créer la popup const modal = document.createElement('div'); modal.className = 'pairing-modal'; modal.innerHTML = `

Add New Device

`; this.shadowRoot?.appendChild(modal); // Ajouter les event listeners const spAddressInput = modal.querySelector('#sp-address') as HTMLInputElement; const spEmojisInput = modal.querySelector('#sp-emojis') as HTMLInputElement; const deviceNameInput = modal.querySelector('#device-name') as HTMLInputElement; const confirmButton = modal.querySelector('.confirm-button'); const cancelButton = modal.querySelector('.cancel-button'); // Mettre à jour les emojis automatiquement spAddressInput?.addEventListener('input', async () => { const emojis = await addressToEmoji(spAddressInput.value); if (spEmojisInput) spEmojisInput.value = emojis; }); // Gérer la confirmation confirmButton?.addEventListener('click', () => { const spAddress = spAddressInput?.value.trim(); const deviceName = deviceNameInput?.value.trim(); const spEmojis = spEmojisInput?.value.trim(); if (!spAddress || !deviceName) { this.showAlert('Please fill in all required fields'); return; } //if (spAddress.length !== 118) { // this.showAlert('SP Address must be exactly 118 characters long'); // return; //} const newRow: Row = { column1: spAddress, column2: deviceName, column3: spEmojis || '' }; const storageKey = STORAGE_KEYS[currentMode]; const rows: Row[] = JSON.parse(localStorage.getItem(storageKey) || '[]'); rows.push(newRow); localStorage.setItem(storageKey, JSON.stringify(rows)); this.updateTableContent(rows); modal.remove(); isAddingRow = false; }); // Gérer l'annulation cancelButton?.addEventListener('click', () => { modal.remove(); isAddingRow = false; }); } // Fonctions de mise à jour de l'interface private updateTableContent(rows: Row[]): void { const tbody = this.shadowRoot?.querySelector('#pairing-table tbody'); if (!tbody) return; tbody.innerHTML = rows.map(row => ` ${row.column2} ${row.column3} QR Code `).join(''); } private confirmRowPairing(): void { if (!currentRow) return; const inputs = currentRow.getElementsByTagName('input'); const values: string[] = Array.from(inputs).map(input => input.value.trim()); // Vérification des champs vides if (values.some(value => value === '')) { this.showAlert('Please fill in all fields'); return; } // Vérification de la longueur de l'adresse SP if (values[0].length !== 118) { this.showAlert('SP Address must be exactly 118 characters long'); return; } const newRow: Row = { column1: values[0], column2: values[1], column3: values[2] }; const storageKey = STORAGE_KEYS[currentMode]; const rows: Row[] = JSON.parse(localStorage.getItem(storageKey) || '[]'); rows.push(newRow); localStorage.setItem(storageKey, JSON.stringify(rows)); isAddingRow = false; currentRow = null; this.resetButtonContainer(); this.updateTableContent(rows); } private cancelRowPairing(): void { if (!currentRow) return; currentRow.remove(); isAddingRow = false; currentRow = null; this.resetButtonContainer(); } private resetButtonContainer(): void { const buttonContainer = this.shadowRoot?.querySelector('.button-container'); if (!buttonContainer) return; buttonContainer.innerHTML = ` `; } private deleteRowPairing(button: HTMLButtonElement): void { const row = button.closest('tr'); if (!row) return; const table = row.closest('tbody'); if (!table) return; const remainingRows = table.getElementsByTagName('tr').length; if (remainingRows <= 2) { this.showAlert('You must keep at least 2 devices paired'); return; } // Créer la modal de confirmation const modal = document.createElement('div'); modal.className = 'confirm-delete-modal'; modal.innerHTML = `

Confirm Deletion

Are you sure you want to delete this device?

`; this.shadowRoot?.appendChild(modal); // Gérer les boutons de la modal const confirmBtn = modal.querySelector('.confirm-btn'); const cancelBtn = modal.querySelector('.cancel-btn'); confirmBtn?.addEventListener('click', () => { // Calculer l'index AVANT de supprimer la ligne du DOM const index = Array.from(table.children).indexOf(row); const storageKey = STORAGE_KEYS[currentMode]; const rows = JSON.parse(localStorage.getItem(storageKey) || '[]'); // Supprimer du localStorage if (index > -1) { rows.splice(index, 1); localStorage.setItem(storageKey, JSON.stringify(rows)); } // Animation et suppression du DOM row.style.transition = 'opacity 0.3s, transform 0.3s'; row.style.opacity = '0'; row.style.transform = 'translateX(-100%)'; setTimeout(() => { row.remove(); }, 300); modal.remove(); }); cancelBtn?.addEventListener('click', () => { modal.remove(); }); } private editDeviceName(cell: HTMLTableCellElement): void { if (cell.classList.contains('editing')) return; const currentValue = cell.textContent || ''; const input = document.createElement('input'); input.type = 'text'; input.value = currentValue; input.className = 'edit-input'; input.addEventListener('blur', () => this.finishEditing(cell, input)); input.addEventListener('keypress', (e: KeyboardEvent) => { if (e.key === 'Enter') { this.finishEditing(cell, input); } }); cell.textContent = ''; cell.appendChild(input); cell.classList.add('editing'); input.focus(); } private async finishEditing(cell: HTMLTableCellElement, input: HTMLInputElement): Promise { const newValue = input.value.trim(); if (newValue === '') { cell.textContent = cell.getAttribute('data-original-value') || ''; cell.classList.remove('editing'); return; } try { const service = await Services.getInstance(); const pairingProcessId = service.getPairingProcessId(); const process = await service.getProcess(pairingProcessId); // Mettre à jour le nom via le service if (process) { await service.updateMemberPublicName(process, newValue); } // Mettre à jour l'interface cell.textContent = newValue; cell.classList.remove('editing'); } catch (error) { console.error('Failed to update name:', error); // Restaurer l'ancienne valeur en cas d'erreur cell.textContent = cell.getAttribute('data-original-value') || ''; cell.classList.remove('editing'); } } // Fonction pour gérer le téléchargement de l'avatar private handleAvatarUpload(event: Event): void { const input = event.target as HTMLInputElement; const file = input.files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (e: ProgressEvent) => { const result = e.target?.result as string; const popupAvatar = this.shadowRoot?.getElementById('popup-avatar-img') as HTMLImageElement; const navAvatar = this.shadowRoot?.querySelector('.nav-wrapper .avatar') as HTMLImageElement; if (popupAvatar) popupAvatar.src = result; if (navAvatar) navAvatar.src = result; localStorage.setItem('userAvatar', result); }; reader.readAsDataURL(file); } } private async showProcessCreation(): Promise { this.hideAllContent(); const container = this.shadowRoot?.getElementById('process-creation-content'); if (container) { getProcessCreation(container); } } private async showDocumentValidation(): Promise { this.hideAllContent(); const container = this.shadowRoot?.getElementById('document-validation-content'); if (container) { getDocumentValidation(container); } } private async showProcess(): Promise { this.hideAllContent(); const container = this.shadowRoot?.getElementById('process-content'); if (container) { const service = await Services.getInstance(); const myProcesses = await service.getMyProcesses(); if (myProcesses && myProcesses.length != 0) { const myProcessesDataUnfiltered: { name: string, publicData: Record }[] = await Promise.all(myProcesses.map(async processId => { const process = await service.getProcess(processId); const lastState = process ? service.getLastCommitedState(process) : null; if (!lastState) { return { name: '', publicData: {} }; } const description = await service.decryptAttribute(processId, lastState, 'description'); const name = description ? description : 'N/A'; const publicData = process ? await service.getPublicData(process) : null; if (!publicData) { return { name: '', publicData: {} }; } return { name: name, publicData: publicData }; })); const myProcessesData = myProcessesDataUnfiltered.filter( (p) => p.name !== '' && Object.keys(p.publicData).length != 0 ); createProcessTab(container, myProcessesData); } else { createProcessTab(container, []); } } } private showProcessNotifications(processName: string): void { const process = mockProcessRows.find(p => p.process === processName); if (!process) return; const modal = document.createElement('div'); modal.className = 'notifications-modal'; let notificationsList = process.notification.messages.map(msg => `
${msg.read ? ` ` : ` ` }
${msg.message} ${msg.date}
`).join(''); if (process.notification.messages.length === 0) { notificationsList = '

No notifications

'; } modal.innerHTML = `

${processName} Notifications

${notificationsList}
`; this.shadowRoot?.appendChild(modal); // Mettre à jour le compteur de notifications const countElement = this.shadowRoot?.querySelector(`.notification-count[data-process="${processName}"]`); if (countElement) { const notifCount = this.calculateNotifications(process.notification.messages); countElement.textContent = `${notifCount.unread}/${notifCount.total}`; } const closeButton = modal.querySelector('.close-notifications'); closeButton?.addEventListener('click', () => { modal.remove(); this.showProcess(); // Rafraîchir l'affichage pour mettre à jour les compteurs }); } private handleLogout(): void { localStorage.clear(); window.location.href = '../login/login.html'; } // Fonctions de gestion des contrats private showContractPopup(contractId: string, event?: Event) { if (event) { event.preventDefault(); } // Check if the contract exists in mockContracts const contract = mockContracts[contractId as keyof typeof mockContracts]; if (!contract) { console.error('Contract not found:', contractId); return; } const popup = document.createElement('div'); popup.className = 'contract-popup-overlay'; popup.innerHTML = `

${contract.title}

Date: ${contract.date}

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

Terms:

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

Content: ${contract.content}

`; this.shadowRoot?.appendChild(popup); const closeBtn = popup.querySelector('.close-contract-popup'); const closePopup = () => popup.remove(); closeBtn?.addEventListener('click', closePopup); popup.addEventListener('click', (e) => { if (e.target === popup) closePopup(); }); } // Fonction utilitaire pour cacher tous les contenus private hideAllContent(): void { const contents = ['pairing-content', 'wallet-content', 'process-content', 'process-creation-content', 'data-content', 'document-validation-content']; contents.forEach(id => { const element = this.shadowRoot?.getElementById(id); if (element) { element.style.display = 'none'; } }); } // Fonctions d'affichage des sections private async showPairing(): Promise { const service = await Services.getInstance(); const spAddress = await service.getDeviceAddress(); isAddingRow = false; currentRow = null; currentMode = 'pairing'; this.hideAllContent(); const headerElement = this.shadowRoot?.getElementById('parameter-header'); if (headerElement) { headerElement.textContent = 'Pairing'; } const pairingContent = this.shadowRoot?.getElementById('pairing-content'); if (pairingContent) { pairingContent.style.display = 'block'; pairingContent.innerHTML = `
Pairing
Device Name SP Emojis QR Code Actions
`; let rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.pairing) || '[]'); const deviceExists = rows.some((row: Row) => row.column1 === spAddress); if (!deviceExists && spAddress) { const emojis = await addressToEmoji(spAddress); try { // Déboguer le processus de pairing const pairingProcessId = await service.getPairingProcessId(); console.log('Pairing Process ID:', pairingProcessId); const pairingProcess = await service.getProcess(pairingProcessId); console.log('Pairing Process:', pairingProcess); const userName = pairingProcess?.states?.[0]?.public_data?.memberPublicName || localStorage.getItem('userName') console.log('Username found:', userName); const newRow = { column1: spAddress, column2: userName, column3: emojis }; rows = [newRow, ...rows]; localStorage.setItem(STORAGE_KEYS.pairing, JSON.stringify(rows)); } catch (error) { console.error('Error getting pairing process:', error); const newRow = { column1: spAddress, column2: 'This Device', column3: emojis }; rows = [newRow, ...rows]; localStorage.setItem(STORAGE_KEYS.pairing, JSON.stringify(rows)); } } this.updateTableContent(rows); } } private showWallet(): void { isAddingRow = false; currentRow = null; currentMode = 'wallet'; this.hideAllContent(); // Mettre à jour le titre const headerTitle = this.shadowRoot?.getElementById('header-title'); if (headerTitle) headerTitle.textContent = 'Wallet'; const walletContent = this.shadowRoot?.getElementById('wallet-content'); if (!walletContent) return; walletContent.style.display = 'block'; walletContent.innerHTML = `
Wallet
Label Wallet Type
`; const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || '[]'); this.updateWalletTableContent(rows); } private updateWalletTableContent(rows: WalletRow[]): void { const tbody = this.shadowRoot?.querySelector('#wallet-table tbody'); if (!tbody) return; tbody.innerHTML = rows.map(row => ` ${row.column1} ${row.column2} ${row.column3} `).join(''); } private showData(): void { //console.log("showData called"); currentMode = 'data'; this.hideAllContent(); const headerTitle = this.shadowRoot?.getElementById('header-title'); if (headerTitle) headerTitle.textContent = 'Data'; const dataContent = this.shadowRoot?.getElementById('data-content'); if (dataContent) { dataContent.style.display = 'block'; dataContent.innerHTML = `
Data
Name Visibility Role Duration Legal Contract
`; const rows = mockDataRows || JSON.parse(localStorage.getItem(STORAGE_KEYS.data) || '[]'); this.updateDataTableContent(rows); } } // Fonctions de gestion du wallet private addWalletRow(): void { if (isAddingRow) return; isAddingRow = true; const table = this.shadowRoot?.getElementById('wallet-table')?.getElementsByTagName('tbody')[0]; if (!table) return; currentRow = table.insertRow(); const placeholders = ['Label', 'Wallet', 'Type']; placeholders.forEach(placeholder => { const cell = currentRow!.insertCell(); const input = document.createElement('input'); input.type = 'text'; input.placeholder = placeholder; input.className = 'edit-input'; cell.appendChild(input); }); // Remplacer le bouton "Add a line" par les boutons de confirmation/annulation const buttonContainer = this.shadowRoot?.querySelector('#wallet-content .button-container'); if (!buttonContainer) return; buttonContainer.innerHTML = `
`; this.updateActionButtons(); } private confirmWalletRow(): void { if (!currentRow) return; const inputs = Array.from(currentRow.getElementsByTagName('input')); const allFieldsFilled = inputs.every(input => input.value.trim() !== ''); if (allFieldsFilled) { const newRow: WalletRow = { column1: inputs[0].value.trim(), column2: inputs[1].value.trim(), column3: inputs[2].value.trim() }; const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || '[]'); rows.push(newRow); localStorage.setItem(STORAGE_KEYS.wallet, JSON.stringify(rows)); isAddingRow = false; currentRow = null; this.showWallet(); } else { this.showAlert('Please complete all fields before confirming.'); } } private cancelWalletRow(): void { if (!currentRow) return; currentRow.remove(); isAddingRow = false; currentRow = null; // Réinitialiser le conteneur de boutons avec le bouton "Add a line" const buttonContainer = this.shadowRoot?.querySelector('#wallet-content .button-container'); if (!buttonContainer) return; buttonContainer.innerHTML = ` `; } private updateDataTableContent(rows: DataRow[]): void { const tbody = this.shadowRoot?.querySelector('#data-table tbody'); if (!tbody) return; tbody.innerHTML = rows.map(row => ` ${row.column1} ${row.column2} ${row.column3} ${row.column4} ${row.column5} ${row.column6} `).join(''); } // Fonctions de gestion de l'avatar et de la bannière private openAvatarPopup(): void { const popup = this.shadowRoot?.getElementById('avatar-popup'); if (!popup) return; // Récuprer les valeurs stockées const savedName = localStorage.getItem('userName'); const savedLastName = localStorage.getItem('userLastName'); const savedAvatar = localStorage.getItem('userAvatar') || 'https://via.placeholder.com/150'; const savedBanner = localStorage.getItem('userBanner') || 'https://via.placeholder.com/800x200'; const savedAddress = localStorage.getItem('userAddress') || '🏠 🌍 🗽🎊😩-🎊😑😩'; popup.innerHTML = ` `; popup.style.display = 'block'; this.setupEventListeners(popup); // Ajouter le gestionnaire d'événements pour la bannière const bannerImg = popup.querySelector('#popup-banner-img'); const bannerInput = popup.querySelector('#banner-upload') as HTMLInputElement; if (bannerImg && bannerInput) { bannerImg.addEventListener('click', () => { bannerInput.click(); }); } const recoveryExported = localStorage.getItem('recoveryExported') === 'true'; if (recoveryExported) { const exportRecoveryBtn = popup.querySelector('.recovery-btn') as HTMLButtonElement; if (exportRecoveryBtn) { exportRecoveryBtn.disabled = true; exportRecoveryBtn.style.opacity = '0.5'; exportRecoveryBtn.style.cursor = 'not-allowed'; } } } private setupEventListeners(popup: HTMLElement): void { // Gestionnaire pour la fermeture const closeBtn = popup.querySelector('.close-popup'); if (closeBtn) { closeBtn.addEventListener('click', () => { popup.style.display = 'none'; }); } // Gestionnaire pour l'upload d'avatar const avatarUpload = popup.querySelector('#avatar-upload') as HTMLInputElement; if (avatarUpload) { avatarUpload.addEventListener('change', (e: Event) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (e: ProgressEvent) => { const result = e.target?.result as string; // Mise à jour de l'avatar dans la preview et le popup const popupAvatar = this.shadowRoot?.getElementById('popup-avatar-img') as HTMLImageElement; const previewAvatar = this.shadowRoot?.querySelector('.preview-avatar') as HTMLImageElement; if (popupAvatar) popupAvatar.src = result; if (previewAvatar) previewAvatar.src = result; localStorage.setItem('userAvatar', result); }; reader.readAsDataURL(file); } }); } // Gestionnaire pour l'upload de bannière const bannerUpload = popup.querySelector('#banner-upload') as HTMLInputElement; if (bannerUpload) { bannerUpload.addEventListener('change', (e: Event) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (e: ProgressEvent) => { const result = e.target?.result as string; // Mise à jour de la bannière dans la preview et le popup const popupBanner = this.shadowRoot?.getElementById('popup-banner-img') as HTMLImageElement; const previewBanner = this.shadowRoot?.querySelector('.preview-banner-img') as HTMLImageElement; if (popupBanner) popupBanner.src = result; if (previewBanner) previewBanner.src = result; localStorage.setItem('userBanner', result); }; reader.readAsDataURL(file); } }); } // Gestionnaires pour les champs de texte const nameInput = popup.querySelector('#userName') as HTMLInputElement; const lastNameInput = popup.querySelector('#userLastName') as HTMLInputElement; if (nameInput) { nameInput.addEventListener('input', () => { const newName = nameInput.value; localStorage.setItem('userName', newName); // Mise à jour du nom dans la preview const previewName = this.shadowRoot?.querySelector('.preview-name'); if (previewName) previewName.textContent = newName; }); } if (lastNameInput) { lastNameInput.addEventListener('input', () => { const newLastName = lastNameInput.value; localStorage.setItem('userLastName', newLastName); // Mise à jour du nom de famille dans la preview const previewLastName = this.shadowRoot?.querySelector('.preview-lastname'); if (previewLastName) previewLastName.textContent = newLastName; }); } } private closeAvatarPopup(): void { const popup = this.shadowRoot?.querySelector('.avatar-popup'); if (popup) popup.remove(); } private loadAvatar(): void { const savedAvatar = localStorage.getItem('userAvatar'); if (savedAvatar) { const avatarImg = this.shadowRoot?.querySelector('.avatar') as HTMLImageElement; if (avatarImg) { avatarImg.src = savedAvatar; } } } private loadUserInfo(): void { const savedName = localStorage.getItem('userName'); const savedLastName = localStorage.getItem('userLastName'); const savedAvatar = localStorage.getItem('userAvatar'); const savedBanner = localStorage.getItem('userBanner'); // Mise à jour du nom dans la preview if (savedName) { const previewName = this.shadowRoot?.querySelector('.preview-name'); if (previewName) { previewName.textContent = savedName; } } // Mise à jour du nom de famille dans la preview if (savedLastName) { const previewLastName = this.shadowRoot?.querySelector('.preview-lastname'); if (previewLastName) { previewLastName.textContent = savedLastName; } } // Mise à jour de l'avatar dans la preview if (savedAvatar) { const previewAvatar = this.shadowRoot?.querySelector('.preview-avatar') as HTMLImageElement; if (previewAvatar) { previewAvatar.src = savedAvatar; } } // Mise à jour de la bannière dans la preview if (savedBanner) { const previewBanner = this.shadowRoot?.querySelector('.preview-banner-img') as HTMLImageElement; if (previewBanner) { previewBanner.src = savedBanner; } } } private updateNavbarName(name: string): void { const nameElement = this.shadowRoot?.querySelector('.nav-wrapper .user-name'); if (nameElement) { nameElement.textContent = name; } } private updateNavbarLastName(lastName: string): void { const lastNameElement = this.shadowRoot?.querySelector('.nav-wrapper .user-lastname'); if (lastNameElement) { lastNameElement.textContent = lastName; } } private updateProfilePreview(data: { avatar?: string, banner?: string, name?: string, lastName?: string }): void { if (data.avatar) { const previewAvatar = this.shadowRoot?.querySelector('.preview-avatar') as HTMLImageElement; if (previewAvatar) previewAvatar.src = data.avatar; } if (data.banner) { const previewBanner = this.shadowRoot?.querySelector('.preview-banner-img') as HTMLImageElement; if (previewBanner) previewBanner.src = data.banner; } if (data.name) { const previewName = this.shadowRoot?.querySelector('.preview-name'); if (previewName) previewName.textContent = data.name; } if (data.lastName) { const previewLastName = this.shadowRoot?.querySelector('.preview-lastname'); if (previewLastName) previewLastName.textContent = data.lastName; } } private initializeEventListeners() { this.shadowRoot?.addEventListener('DOMContentLoaded', () => { this.showPairing(); }); const editableFields = this.shadowRoot?.querySelectorAll('.editable'); if (editableFields) { editableFields.forEach(field => { field.addEventListener('click', () => { if (!field.classList.contains('editing')) { const currentValue = field.textContent || ''; const input = document.createElement('input'); input.type = 'text'; input.value = currentValue; input.className = 'edit-input'; field.textContent = ''; field.appendChild(input); field.classList.add('editing'); input.focus(); } }); }); } const avatarInput = this.shadowRoot?.getElementById('avatar-upload') as HTMLInputElement; if (avatarInput) { avatarInput.addEventListener('change', this.handleAvatarUpload.bind(this)); } } private showQRCodeModal(pairingId: string): void { const modal = document.createElement('div'); modal.className = 'qr-modal'; modal.innerHTML = `
× QR Code Large
${decodeURIComponent(pairingId)}
`; this.shadowRoot?.appendChild(modal); const closeBtn = modal.querySelector('.close-qr-modal'); closeBtn?.addEventListener('click', () => modal.remove()); modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); }); } } customElements.define('account-element', AccountElement); export { AccountElement };