From 33935f4b186d064555661c18612d50ce5c976c53 Mon Sep 17 00:00:00 2001 From: NicolasCantu Date: Thu, 23 Oct 2025 20:20:51 +0200 Subject: [PATCH] feat: Improve UI layout and add account deletion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivations :** - Make status field same width as authentication button - Add secure account deletion functionality - Improve visual consistency of interface **Modifications :** - Adjusted status container width to match button width - Added red delete account button at bottom - Implemented secure account deletion with double confirmation - Added comprehensive data cleanup (credentials, storage, IndexedDB, caches) - Enhanced CSS styling for status indicator and danger button **Pages affectรฉes :** - src/pages/home/home.html - Added delete account button - src/pages/home/home.ts - Added account deletion logic - src/4nk.css - Enhanced styling for status and danger button --- src/4nk.css | 49 +++++++++++++++ src/pages/home/home.html | 4 ++ src/pages/home/home.ts | 126 +++++++++++++++++++++++++++++++++++---- 3 files changed, 167 insertions(+), 12 deletions(-) diff --git a/src/4nk.css b/src/4nk.css index 53a2968..84240e6 100644 --- a/src/4nk.css +++ b/src/4nk.css @@ -1181,6 +1181,55 @@ select[data-multi-select-plugin] { right: 0; } +/* Status Container - Match button width */ +.status-container { + width: 100%; + margin: 20px 0; +} + +.status-indicator { + width: 100%; + text-align: center; + padding: 15px; + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 8px; + font-size: 14px; + color: #495057; +} + +/* Account Actions */ +.account-actions { + margin-top: 30px; + padding-top: 20px; + border-top: 1px solid #e9ecef; + text-align: center; +} + +.danger-btn { + background: #dc3545; + color: white; + border: none; + padding: 12px 24px; + border-radius: 8px; + cursor: pointer; + font-size: 14px; + font-weight: 500; + transition: all 0.3s ease; + box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3); +} + +.danger-btn:hover { + background: #c82333; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(220, 53, 69, 0.4); +} + +.danger-btn:active { + transform: translateY(0); + box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3); +} + /* Responsive Design for Mode Selection */ @media (max-width: 768px) { .mode-buttons { diff --git a/src/pages/home/home.html b/src/pages/home/home.html index 6cc37aa..1bd4749 100755 --- a/src/pages/home/home.html +++ b/src/pages/home/home.html @@ -16,6 +16,10 @@ + +
+ +
diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts index 70144ff..5180d93 100755 --- a/src/pages/home/home.ts +++ b/src/pages/home/home.ts @@ -120,6 +120,9 @@ export async function initHomePage(): Promise { // Set up main pairing interface setupMainPairing(); + + // Set up account actions + setupAccountActions(); const container = getCorrectDOM('login-4nk-component') as HTMLElement; container.querySelectorAll('.tab').forEach(tab => { @@ -520,10 +523,10 @@ export function setupIframePairingButtons() { // Main Pairing Interface export function setupMainPairing(): void { const container = getCorrectDOM('login-4nk-component') as HTMLElement; - + const mainPairingButton = container.querySelector('#mainPairingButton') as HTMLButtonElement; const mainStatus = container.querySelector('#main-status') as HTMLElement; - + if (mainPairingButton) { mainPairingButton.addEventListener('click', async () => { await handleMainPairing(); @@ -535,7 +538,7 @@ async function handleMainPairing(): Promise { const container = getCorrectDOM('login-4nk-component') as HTMLElement; const mainStatus = container.querySelector('#main-status') as HTMLElement; const mainPairingButton = container.querySelector('#mainPairingButton') as HTMLButtonElement; - + try { // Update UI if (mainStatus) { @@ -550,16 +553,16 @@ async function handleMainPairing(): Promise { const service = await Services.getInstance(); const { secureCredentialsService } = await import('../../services/secure-credentials.service'); const hasCredentials = await secureCredentialsService.hasCredentials(); - + if (hasCredentials) { // Existing pairing - decrypt credentials console.log('๐Ÿ”“ Existing credentials found, decrypting...'); if (mainStatus) { mainStatus.innerHTML = '
Decrypting existing credentials...'; } - + await secureCredentialsService.retrieveCredentials(''); // Empty password for WebAuthn - + if (mainStatus) { mainStatus.innerHTML = 'โœ… Credentials decrypted successfully'; } @@ -569,31 +572,130 @@ async function handleMainPairing(): Promise { if (mainStatus) { mainStatus.innerHTML = '
Creating new secure pairing...'; } - + // This will trigger the WebAuthn flow and create new credentials await prepareAndSendPairingTx(); - + if (mainStatus) { mainStatus.innerHTML = 'โœ… New pairing created successfully'; } } - + // Re-enable button if (mainPairingButton) { mainPairingButton.disabled = false; mainPairingButton.textContent = 'Authenticate with Browser'; } - + } catch (error) { console.error('Pairing failed:', error); - + if (mainStatus) { mainStatus.innerHTML = 'โŒ Authentication failed'; } - + if (mainPairingButton) { mainPairingButton.disabled = false; mainPairingButton.textContent = 'Authenticate with Browser'; } } } + +// Account Actions +export function setupAccountActions(): void { + const container = getCorrectDOM('login-4nk-component') as HTMLElement; + + const deleteAccountButton = container.querySelector('#deleteAccountButton') as HTMLButtonElement; + + if (deleteAccountButton) { + deleteAccountButton.addEventListener('click', async () => { + await handleDeleteAccount(); + }); + } +} + +async function handleDeleteAccount(): Promise { + const container = getCorrectDOM('login-4nk-component') as HTMLElement; + const mainStatus = container.querySelector('#main-status') as HTMLElement; + + // Confirmation dialog + const confirmed = confirm( + 'โš ๏ธ WARNING: This will permanently delete your account and all associated data.\n\n' + + 'This action cannot be undone!\n\n' + + 'Are you sure you want to delete your account?' + ); + + if (!confirmed) { + return; + } + + // Double confirmation + const doubleConfirmed = confirm( + '๐Ÿšจ FINAL WARNING: You are about to permanently delete your account.\n\n' + + 'All your data, credentials, and pairings will be lost forever.\n\n' + + 'Type "DELETE" to confirm (case sensitive):' + ); + + if (doubleConfirmed) { + const userInput = prompt('Type "DELETE" to confirm account deletion:'); + if (userInput !== 'DELETE') { + if (mainStatus) { + mainStatus.innerHTML = 'โŒ Account deletion cancelled - confirmation text did not match'; + } + return; + } + } else { + return; + } + + try { + if (mainStatus) { + mainStatus.innerHTML = '
Deleting account and all data...'; + } + + // Get services + const service = await Services.getInstance(); + const { secureCredentialsService } = await import('../../services/secure-credentials.service'); + + // Delete all credentials + await secureCredentialsService.deleteCredentials(); + + // Clear all local storage + localStorage.clear(); + sessionStorage.clear(); + + // Clear IndexedDB + if ('indexedDB' in window) { + const databases = await indexedDB.databases(); + for (const db of databases) { + if (db.name) { + indexedDB.deleteDatabase(db.name); + } + } + } + + // Clear service worker caches + if ('caches' in window) { + const cacheNames = await caches.keys(); + await Promise.all( + cacheNames.map(cacheName => caches.delete(cacheName)) + ); + } + + if (mainStatus) { + mainStatus.innerHTML = 'โœ… Account and all data deleted successfully'; + } + + // Reload the page to start fresh + setTimeout(() => { + window.location.reload(); + }, 2000); + + } catch (error) { + console.error('Account deletion failed:', error); + + if (mainStatus) { + mainStatus.innerHTML = 'โŒ Failed to delete account'; + } + } +}