UNPKG

besper-frontend-site-dev-main

Version:

Professional B-esper Frontend Site - Site-wide integration toolkit for full website bot deployment

422 lines (374 loc) 12.4 kB
/** * Credentials Display Component * Handles the display and management of bot credentials */ export class CredentialsDisplay { constructor(widget, state, i18n = null) { this.widget = widget; this.state = state; this.i18n = i18n; this.isSecretVisible = false; } /** * Update language * @param {string} language - New language code */ updateLanguage(language) { if (this.i18n) { console.log(`[CredentialsDisplay] Language updated to: ${language}`); } } /** * Get translated text * @param {string} key - Translation key * @param {Object} variables - Variables for substitution * @returns {string} Translated text */ t(key, variables = {}) { return this.i18n ? this.i18n.t(key, variables) : key; } /** * Generate the HTML for credentials display * @param {Object} credentials - Credentials object * @returns {string} Credentials HTML string */ getHTML(credentials) { return ` <div class="bm-credentials-container"> <div class="bm-form-group"> <label class="bm-form-label">${this.t('configuration.credentials.botId')}</label> <div class="bm-credential-field"> <input type="text" class="bm-form-input" id="bm-botIdInput" readonly value="${credentials.botId || ''}"> <button class="bm-copy-btn" data-copy-target="bm-botIdInput" title="${this.t('tooltips.copyBotId')}"> ${this.getCopyIcon()} </button> </div> </div> <div class="bm-form-group"> <label class="bm-form-label">${this.t('configuration.credentials.managementId')}</label> <div class="bm-credential-field"> <input type="text" class="bm-form-input" id="bm-managementIdInput" readonly value="${credentials.managementId || ''}"> <button class="bm-copy-btn" data-copy-target="bm-managementIdInput" title="${this.t('tooltips.copyManagementId')}"> ${this.getCopyIcon()} </button> </div> </div> <div class="bm-form-group"> <label class="bm-form-label">${this.t('configuration.credentials.managementSecret')}</label> <div class="bm-credential-field"> <input type="password" class="bm-form-input" id="bm-managementSecretInput" readonly value="${credentials.managementSecret || ''}"> <button class="bm-toggle-btn" id="bm-toggleSecretBtn" title="${this.t('tooltips.toggleVisibility')}"> ${this.getEyeIcon()} </button> <button class="bm-copy-btn" data-copy-target="bm-managementSecretInput" title="${this.t('tooltips.copyManagementSecret')}"> ${this.getCopyIcon()} </button> </div> </div> </div> `; } /** * Get copy icon SVG */ getCopyIcon() { return ` <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/> <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/> </svg> `; } /** * Get eye icon SVG */ getEyeIcon() { return ` <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/> <circle cx="12" cy="12" r="3"/> </svg> `; } /** * Get eye off icon SVG */ getEyeOffIcon() { return ` <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/> <line x1="1" y1="1" x2="23" y2="23"/> </svg> `; } /** * Setup event listeners for credentials display * @param {Object} callbacks - Callback functions for events */ setupEventListeners(callbacks = {}) { // Copy buttons const copyButtons = this.widget.querySelectorAll( '.bm-copy-btn[data-copy-target]' ); copyButtons.forEach(btn => { btn.addEventListener('click', () => { const targetId = btn.getAttribute('data-copy-target'); this.copyToClipboard(targetId, callbacks.onCopy); }); }); // Toggle secret visibility const toggleSecretBtn = this.widget.querySelector('#bm-toggleSecretBtn'); if (toggleSecretBtn) { toggleSecretBtn.addEventListener('click', () => { this.toggleSecretVisibility(callbacks.onToggleVisibility); }); } } /** * Copy text to clipboard * @param {string} targetId - ID of the input element to copy from * @param {Function} callback - Optional callback function */ copyToClipboard(targetId, callback) { const input = this.widget.querySelector(`#${targetId}`); if (!input) return; // Select the text input.select(); input.setSelectionRange(0, 99999); // For mobile devices try { // Try modern clipboard API first if (navigator.clipboard && window.isSecureContext) { navigator.clipboard .writeText(input.value) .then(() => { this.showCopyFeedback(input); if (callback) callback(targetId, input.value); }) .catch(() => { // Fallback to execCommand this.fallbackCopy(input, targetId, callback); }); } else { // Fallback to execCommand this.fallbackCopy(input, targetId, callback); } } catch (err) { console.error('Copy failed:', err); } } /** * Fallback copy method using execCommand * @param {HTMLElement} input - Input element * @param {string} targetId - Target ID * @param {Function} callback - Optional callback function */ fallbackCopy(input, targetId, callback) { try { const successful = document.execCommand('copy'); if (successful) { this.showCopyFeedback(input); if (callback) callback(targetId, input.value); } } catch (err) { console.error('Fallback copy failed:', err); } } /** * Show copy feedback * @param {HTMLElement} input - Input element that was copied */ showCopyFeedback(input) { const originalBorder = input.style.border; const originalBackground = input.style.background; // Visual feedback input.style.border = '2px solid #10b981'; input.style.background = '#f0fdf4'; // Find associated copy button and show feedback const credentialField = input.closest('.bm-credential-field'); const copyBtn = credentialField?.querySelector('.bm-copy-btn'); if (copyBtn) { const originalContent = copyBtn.innerHTML; copyBtn.innerHTML = ` <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <polyline points="20 6 9 17 4 12"/> </svg> `; copyBtn.style.background = '#10b981'; copyBtn.style.color = 'white'; setTimeout(() => { copyBtn.innerHTML = originalContent; copyBtn.style.background = ''; copyBtn.style.color = ''; }, 2000); } setTimeout(() => { input.style.border = originalBorder; input.style.background = originalBackground; }, 2000); } /** * Toggle secret visibility * @param {Function} callback - Optional callback function */ toggleSecretVisibility(callback) { const secretInput = this.widget.querySelector('#bm-managementSecretInput'); const toggleBtn = this.widget.querySelector('#bm-toggleSecretBtn'); if (!secretInput || !toggleBtn) return; this.isSecretVisible = !this.isSecretVisible; // Toggle input type secretInput.type = this.isSecretVisible ? 'text' : 'password'; // Update button icon toggleBtn.innerHTML = this.isSecretVisible ? this.getEyeOffIcon() : this.getEyeIcon(); toggleBtn.title = this.isSecretVisible ? 'Hide secret' : 'Show secret'; if (callback) { callback(this.isSecretVisible); } } /** * Update credentials data * @param {Object} credentials - New credentials */ updateCredentials(credentials) { const botIdInput = this.widget.querySelector('#bm-botIdInput'); const managementIdInput = this.widget.querySelector( '#bm-managementIdInput' ); const managementSecretInput = this.widget.querySelector( '#bm-managementSecretInput' ); if (botIdInput) botIdInput.value = credentials.botId || ''; if (managementIdInput) managementIdInput.value = credentials.managementId || ''; if (managementSecretInput) managementSecretInput.value = credentials.managementSecret || ''; } /** * Get current credentials data * @returns {Object} Current credentials */ getCredentials() { const botIdInput = this.widget.querySelector('#bm-botIdInput'); const managementIdInput = this.widget.querySelector( '#bm-managementIdInput' ); const managementSecretInput = this.widget.querySelector( '#bm-managementSecretInput' ); return { botId: botIdInput?.value || '', managementId: managementIdInput?.value || '', managementSecret: managementSecretInput?.value || '', }; } /** * Validate credentials * @returns {Object} Validation result */ validateCredentials() { const credentials = this.getCredentials(); const errors = []; if (!credentials.botId) { errors.push('Bot ID is required'); } if (!credentials.managementId) { errors.push('Management ID is required'); } if (!credentials.managementSecret) { errors.push('Management Secret is required'); } // Validate format (basic validation) if (credentials.botId && !/^[a-zA-Z0-9-_]+$/.test(credentials.botId)) { errors.push('Bot ID contains invalid characters'); } if ( credentials.managementId && !/^[a-zA-Z0-9-_]+$/.test(credentials.managementId) ) { errors.push('Management ID contains invalid characters'); } return { isValid: errors.length === 0, errors, credentials, }; } /** * Show validation errors * @param {Array} errors - Array of error messages */ showValidationErrors(errors) { // Remove existing error messages this.clearValidationErrors(); errors.forEach((error, index) => { const errorElement = document.createElement('div'); errorElement.className = 'bm-credential-error'; errorElement.textContent = error; errorElement.id = `bm-credential-error-${index}`; const container = this.widget.querySelector('.bm-credentials-container'); if (container) { container.appendChild(errorElement); } }); } /** * Clear validation errors */ clearValidationErrors() { const errors = this.widget.querySelectorAll('.bm-credential-error'); errors.forEach(error => error.remove()); } /** * Mask credentials for display * @param {Object} credentials - Credentials to mask * @returns {Object} Masked credentials */ maskCredentials(credentials) { return { botId: credentials.botId, managementId: credentials.managementId, managementSecret: credentials.managementSecret ? credentials.managementSecret.replace(/./g, '•') : '', }; } /** * Export credentials (for backup/migration) * @returns {string} JSON string of credentials */ exportCredentials() { const credentials = this.getCredentials(); return JSON.stringify(credentials, null, 2); } /** * Import credentials from JSON * @param {string} jsonString - JSON string of credentials * @returns {boolean} Success status */ importCredentials(jsonString) { try { const credentials = JSON.parse(jsonString); this.updateCredentials(credentials); return true; } catch (error) { console.error('Failed to import credentials:', error); return false; } } /** * Reset secret visibility to default (hidden) */ resetSecretVisibility() { if (this.isSecretVisible) { this.toggleSecretVisibility(); } } /** * Cleanup method for component destruction */ destroy() { this.clearValidationErrors(); this.resetSecretVisibility(); } }