UNPKG

besper-frontend-site-dev-main

Version:

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

1,044 lines (949 loc) 64.1 kB
/** * Implementation Tab Component * Handles the implementation tab in the bot management interface * This tab provides code examples and documentation for integrating the chat widget */ // Import managementApiCall statically to avoid code splitting import { managementApiCall } from '../../../services/centralizedApi.js'; export class ImplementationTab { constructor(widget, state, options = {}) { this.widget = widget; this.state = state; this.options = options; this.i18n = options.i18n; // Store reference to bot management for API access this.widget.botManagement = options.botManagement || null; } /** * Update language * @param {string} language - New language code */ updateLanguage(language) { if (this.i18n) { console.log(`[ImplementationTab] Language updated to: ${language}`); // Re-render implementation content if needed } } /** * 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(`implementation.${key}`, variables) : key; } /** * Generate the HTML for the implementation tab * @returns {string} Implementation tab HTML string */ getHTML() { return ` <div class="kb-container" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #fafbfc; padding: 40px 20px; color: #1a1f2c; line-height: 1.5; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;"> <div style="max-width: 1400px; margin: 0 auto;"> <!-- Header --> <div class="kb-header" style="margin-bottom: 40px;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px;"> <h1 style="font-size: 24px; font-weight: 500; color: #022d54; letter-spacing: -0.02em; margin: 0;">${this.t('title')}</h1> <div style="display: flex; align-items: center; gap: 12px; font-size: 13px; color: #6b7684;"> <span style="font-variant-numeric: tabular-nums;">${this.t('integrationGuide')}</span> </div> </div> <!-- Development Features Toggle --> <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 24px; padding: 16px; background: white; border: 1px solid #e1e7ef; border-radius: 4px;"> <label style="display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; color: #1a1f2c; font-weight: 500;"> <input type="checkbox" id="kb-dev-features-toggle" style="margin: 0;"> <span>${this.t('developmentFeatures.preview')}</span> </label> <div style="font-size: 13px; color: #6b7684;">${this.t('developmentFeatures.description')}</div> </div> </div> <!-- Content Area --> <div id="impl-content"> <!-- Widget Implementation --> ${this.getWidgetImplementationCard()} <!-- Container Implementation --> ${this.getContainerImplementationCard()} <!-- WordPress Integration (Hidden by Default) --> <div id="kb-wordpress-section" style="display: none;"> ${this.getWordPressIntegrationCard()} </div> <!-- CORS Configuration --> ${this.getCORSConfigurationCard()} <!-- Documentation Links --> ${this.getDocumentationCard()} </div> </div> </div> `; } /** * Get Widget Implementation card HTML */ getWidgetImplementationCard() { return ` <div class="kb-implementation-card" style="background: white; border: 1px solid #e1e7ef; border-radius: 4px; overflow: hidden; margin-bottom: 24px;"> <div class="kb-table-header" style="padding: 20px 24px; border-bottom: 1px solid #e1e7ef; display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; align-items: center; gap: 12px;"> <div style="width: 40px; height: 40px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #022d54;"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <polyline points="16 18 22 12 16 6"/> <polyline points="8 6 2 12 8 18"/> </svg> </div> <div> <h3 style="font-size: 16px; font-weight: 500; color: #1a1f2c; margin: 0;">${this.t('widgetImplementation.title')}</h3> <p style="font-size: 14px; color: #6b7684; margin: 4px 0 0 0;">${this.t('widgetImplementation.description')}</p> </div> </div> <button class="kb-copy-button" data-copy-target="kb-widgetCode" title="Copy to clipboard" style="display: flex; align-items: center; gap: 6px; padding: 8px 16px; background: #022d54; border: 1px solid #022d54; border-radius: 4px; cursor: pointer; font-size: 12px; color: white; font-weight: 500; transition: all 0.2s ease;"> <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> <span class="kb-copy-text">${this.t('instructions.copy')}</span> </button> </div> <div style="padding: 24px;"> <div style="margin-bottom: 20px;"> <p style="font-size: 14px; color: #6b7684; margin: 0; line-height: 1.5;">Add this code to your website to display a floating chat button in the bottom-right corner. The widget automatically handles styling, positioning, and user interactions.</p> </div> <div style="border: 1px solid #e1e7ef; border-radius: 4px; overflow: hidden; background: #fafbfc;"> <div style="background: #fafbfc; padding: 12px 16px; border-bottom: 1px solid #e1e7ef; display: flex; align-items: center; justify-content: between;"> <div style="display: flex; align-items: center; gap: 8px;"> <div style="font-size: 12px; font-weight: 600; color: #6b7684; text-transform: uppercase; letter-spacing: 0.05em;">HTML</div> <div style="font-size: 13px; color: #6b7684;">Complete Implementation</div> </div> </div> <div style="background: white; padding: 16px;"> <pre style="margin: 0; font-family: 'SFMono-Regular', 'Monaco', 'Inconsolata', 'Liberation Mono', 'Consolas', monospace; font-size: 13px; line-height: 1.45; color: #1a1f2c; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto;" id="kb-widgetCode"><code>Loading implementation code...</code></pre> </div> </div> </div> </div> `; } /** * Get Container Implementation card HTML */ getContainerImplementationCard() { return ` <div class="kb-implementation-card" style="background: white; border: 1px solid #e1e7ef; border-radius: 4px; overflow: hidden; margin-bottom: 24px;"> <div class="kb-table-header" style="padding: 20px 24px; border-bottom: 1px solid #e1e7ef; display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; align-items: center; gap: 12px;"> <div style="width: 40px; height: 40px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #022d54;"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <rect x="2" y="3" width="20" height="14" rx="2" ry="2"/> <line x1="8" y1="21" x2="16" y2="21"/> <line x1="12" y1="17" x2="12" y2="21"/> </svg> </div> <div> <h3 style="font-size: 16px; font-weight: 500; color: #1a1f2c; margin: 0;">Container Implementation (Embedded Chat)</h3> <p style="font-size: 14px; color: #6b7684; margin: 4px 0 0 0;">Embed the chat interface directly into your page layout</p> </div> </div> <button class="kb-copy-button" data-copy-target="kb-containerCode" title="Copy to clipboard" style="display: flex; align-items: center; gap: 6px; padding: 8px 16px; background: #022d54; border: 1px solid #022d54; border-radius: 4px; cursor: pointer; font-size: 12px; color: white; font-weight: 500; transition: all 0.2s ease;"> <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> <span class="kb-copy-text">Copy Code</span> </button> </div> <div style="padding: 24px;"> <div style="margin-bottom: 20px;"> <p style="font-size: 14px; color: #6b7684; margin: 0; line-height: 1.5;">Embed the chat interface directly into a specific container on your page. Perfect for dedicated support pages or as part of your application interface.</p> </div> <div style="border: 1px solid #e1e7ef; border-radius: 4px; overflow: hidden; background: #fafbfc;"> <div style="background: #fafbfc; padding: 12px 16px; border-bottom: 1px solid #e1e7ef; display: flex; align-items: center; justify-content: between;"> <div style="display: flex; align-items: center; gap: 8px;"> <div style="font-size: 12px; font-weight: 600; color: #6b7684; text-transform: uppercase; letter-spacing: 0.05em;">HTML</div> <div style="font-size: 13px; color: #6b7684;">Embedded Implementation</div> </div> </div> <div style="background: white; padding: 16px;"> <pre style="margin: 0; font-family: 'SFMono-Regular', 'Monaco', 'Inconsolata', 'Liberation Mono', 'Consolas', monospace; font-size: 13px; line-height: 1.45; color: #1a1f2c; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto;" id="kb-containerCode"><code>Loading implementation code...</code></pre> </div> </div> </div> </div> `; } /** * Get WordPress Integration card HTML */ getWordPressIntegrationCard() { return ` <div class="kb-implementation-card" style="background: white; border: 1px solid #e1e7ef; border-radius: 4px; overflow: hidden; margin-bottom: 24px;"> <div class="kb-table-header" style="padding: 20px 24px; border-bottom: 1px solid #e1e7ef; display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; align-items: center; gap: 12px;"> <div style="width: 40px; height: 40px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #022d54;"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="10"/> <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/> <path d="M2 12h20"/> </svg> </div> <div> <h3 style="font-size: 16px; font-weight: 500; color: #1a1f2c; margin: 0;">WordPress Integration</h3> <p style="font-size: 14px; color: #6b7684; margin: 4px 0 0 0;">Easy integration for WordPress websites</p> </div> </div> <div style="display: flex; align-items: center; gap: 8px;"> <span style="padding: 4px 8px; background: #fef3c7; color: #92400e; border-radius: 4px; font-size: 12px; font-weight: 500;">Coming Soon</span> <button class="kb-copy-button" data-copy-target="kb-wordpressCode" title="Copy to clipboard" style="display: flex; align-items: center; gap: 6px; padding: 8px 16px; background: #022d54; border: 1px solid #022d54; border-radius: 4px; cursor: pointer; font-size: 12px; color: white; font-weight: 500; transition: all 0.2s ease;"> <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> <span class="kb-copy-text">Copy Code</span> </button> </div> </div> <div style="padding: 24px;"> <div style="margin-bottom: 20px;"> <p style="font-size: 14px; color: #6b7684; margin: 0; line-height: 1.5;">Integrate B-esper chat seamlessly into your WordPress website using our official plugin or custom implementation methods.</p> </div> <!-- WordPress Plugin Method --> <div style="margin-bottom: 24px; padding: 20px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;"> <h4 style="font-size: 14px; font-weight: 600; color: #1a1f2c; margin: 0; display: flex; align-items: center; gap: 8px;"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="3"/> <path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"/> </svg> Official WordPress Plugin (Recommended) </h4> <span style="padding: 4px 8px; background: #dbeafe; color: #1e40af; border-radius: 4px; font-size: 12px; font-weight: 500;">In Development</span> </div> <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px;"> <div style="display: flex; align-items: center; gap: 8px; font-size: 13px; color: #6b7684;"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M20 6L9 17l-5-5"/> </svg> <span>One-click installation from WordPress repository</span> </div> <div style="display: flex; align-items: center; gap: 8px; font-size: 13px; color: #6b7684;"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M20 6L9 17l-5-5"/> </svg> <span>Visual configuration through WordPress admin</span> </div> <div style="display: flex; align-items: center; gap: 8px; font-size: 13px; color: #6b7684;"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M20 6L9 17l-5-5"/> </svg> <span>Automatic updates and security patches</span> </div> <div style="display: flex; align-items: center; gap: 8px; font-size: 13px; color: #6b7684;"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M20 6L9 17l-5-5"/> </svg> <span>Integration with WordPress user system</span> </div> </div> </div> <!-- Manual Integration Method --> <div style="margin-bottom: 24px; padding: 20px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;"> <h4 style="font-size: 14px; font-weight: 600; color: #1a1f2c; margin: 0; display: flex; align-items: center; gap: 8px;"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <polyline points="16 18 22 12 16 6"/> <polyline points="8 6 2 12 8 18"/> </svg> Manual Integration (Available Now) </h4> <span style="padding: 4px 8px; background: #dcfce7; color: #15803d; border-radius: 4px; font-size: 12px; font-weight: 500;">Available</span> </div> <p style="font-size: 13px; color: #6b7684; margin: 0 0 16px 0;">Add the chat widget to your WordPress theme manually using one of these methods:</p> <div style="border: 1px solid #e1e7ef; border-radius: 4px; overflow: hidden; background: #fafbfc;"> <div style="background: #fafbfc; padding: 12px 16px; border-bottom: 1px solid #e1e7ef; display: flex; align-items: center; justify-content: between;"> <div style="display: flex; align-items: center; gap: 8px;"> <div style="font-size: 12px; font-weight: 600; color: #6b7684; text-transform: uppercase; letter-spacing: 0.05em;">PHP</div> <div style="font-size: 13px; color: #6b7684;">Add to functions.php</div> </div> </div> <div style="background: white; padding: 16px;"> <pre style="margin: 0; font-family: 'SFMono-Regular', 'Monaco', 'Inconsolata', 'Liberation Mono', 'Consolas', monospace; font-size: 13px; line-height: 1.45; color: #1a1f2c; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto;" id="kb-wordpressCode"><code>Loading WordPress integration code...</code></pre> </div> </div> <div style="margin-top: 16px; display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 12px;"> <div style="padding: 12px; background: white; border: 1px solid #e1e7ef; border-radius: 4px;"> <div style="font-size: 13px; font-weight: 600; color: #1a1f2c; margin-bottom: 4px;">Option 1: Theme Functions</div> <div style="font-size: 12px; color: #6b7684;">Add code to your theme's functions.php file (shown above)</div> </div> <div style="padding: 12px; background: white; border: 1px solid #e1e7ef; border-radius: 4px;"> <div style="font-size: 13px; font-weight: 600; color: #1a1f2c; margin-bottom: 4px;">Option 2: Custom HTML Block</div> <div style="font-size: 12px; color: #6b7684;">Use WordPress Gutenberg custom HTML block with the widget implementation code</div> </div> <div style="padding: 12px; background: white; border: 1px solid #e1e7ef; border-radius: 4px;"> <div style="font-size: 13px; font-weight: 600; color: #1a1f2c; margin-bottom: 4px;">Option 3: Page Builder Integration</div> <div style="font-size: 12px; color: #6b7684;">Add as custom HTML/JavaScript in Elementor, Divi, or other page builders</div> </div> </div> </div> </div> </div> `; } /** * Get CORS Configuration card HTML */ getCORSConfigurationCard() { return ` <div class="kb-implementation-card" style="background: white; border: 1px solid #e1e7ef; border-radius: 4px; overflow: hidden; margin-bottom: 24px;"> <div class="kb-table-header" style="padding: 20px 24px; border-bottom: 1px solid #e1e7ef; display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; align-items: center; gap: 12px;"> <div style="width: 40px; height: 40px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #022d54;"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/> </svg> </div> <div> <h3 style="font-size: 16px; font-weight: 500; color: #1a1f2c; margin: 0;">CORS Configuration</h3> <p style="font-size: 14px; color: #6b7684; margin: 4px 0 0 0;">Configure allowed origins for your bot operations</p> </div> </div> </div> <div style="padding: 24px;"> <div style="margin-bottom: 24px;"> <h4 style="color: #1a1f2c; margin: 0 0 8px 0; font-size: 14px; font-weight: 600;">What is CORS?</h4> <p style="color: #6b7684; margin: 0 0 16px 0; font-size: 13px; line-height: 1.5;"> Cross-Origin Resource Sharing (CORS) controls which websites can embed and interact with your bot. This prevents unauthorized usage and protects against abuse. </p> </div> <div style="margin-bottom: 24px;"> <h4 style="color: #1a1f2c; margin: 0 0 12px 0; font-size: 14px; font-weight: 600;">Current Configuration</h4> <div id="kb-corsCurrentStatus" style="background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px; padding: 12px 16px;"> <div style="color: #6b7684; font-size: 13px; display: flex; align-items: center; gap: 8px;"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M21 12a9 9 0 11-6.219-8.56"/> </svg> Loading CORS configuration... </div> </div> </div> <div style="margin-bottom: 24px;"> <h4 style="color: #1a1f2c; margin: 0 0 12px 0; font-size: 14px; font-weight: 600;">Add Allowed Origin</h4> <div style="display: flex; gap: 8px; margin-bottom: 16px;"> <input type="url" id="kb-impl-corsInput" placeholder="https://example.com" style="flex: 1; padding: 8px 12px; border: 1px solid #e1e7ef; border-radius: 4px; font-size: 13px; color: #1a1f2c;" title="Enter a valid URL including protocol (https://)" /> <button id="kb-impl-addOriginBtn" style="padding: 8px 16px; background: #022d54; color: white; border: 1px solid #022d54; border-radius: 4px; font-size: 13px; cursor: pointer; white-space: nowrap; font-weight: 500;" title="Add origin" > Add Origin </button> </div> <div id="kb-impl-corsOriginsList" style="display: flex; flex-direction: column; gap: 8px;"> <!-- CORS origins will be populated here --> </div> <div style="margin-top: 16px; display: flex; gap: 8px;"> <button id="kb-impl-saveCorsBtn" style="padding: 10px 20px; background: #16a34a; color: white; border: 1px solid #16a34a; border-radius: 4px; font-size: 13px; cursor: pointer; font-weight: 600; display: flex; align-items: center; gap: 6px;" title="Save CORS configuration" > <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/> <polyline points="17 21 17 13 7 13 7 21"/> <polyline points="7 3 7 8 15 8"/> </svg> Save Configuration </button> </div> </div> </div> </div> `; } /** * Get Documentation card HTML */ getDocumentationCard() { const envInfo = this.getEnvironmentInfo(); return ` <div class="kb-implementation-card" style="background: white; border: 1px solid #e1e7ef; border-radius: 4px; overflow: hidden; margin-bottom: 24px;"> <div class="kb-table-header" style="padding: 20px 24px; border-bottom: 1px solid #e1e7ef; display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; align-items: center; gap: 12px;"> <div style="width: 40px; height: 40px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #022d54;"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M14 2H6A2 2 0 0 0 4 4V20A2 2 0 0 0 6 22H18A2 2 0 0 0 20 20V8Z"/> <path d="M14 2V8H20"/> <line x1="16" y1="13" x2="8" y2="13"/> <line x1="16" y1="17" x2="8" y2="17"/> <line x1="10" y1="9" x2="8" y2="9"/> </svg> </div> <div> <h3 style="font-size: 16px; font-weight: 500; color: #1a1f2c; margin: 0;">Documentation & Resources</h3> <p style="font-size: 14px; color: #6b7684; margin: 4px 0 0 0;">Package information and additional resources</p> </div> </div> </div> <div style="padding: 24px;"> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px;"> <div style="padding: 20px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; gap: 16px;"> <div style="width: 48px; height: 48px; background: white; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #022d54;"> <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> <path d="M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0H1.763zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z"/> </svg> </div> <div style="flex: 1;"> <h4 style="font-size: 14px; font-weight: 600; color: #1a1f2c; margin: 0 0 4px 0;">NPM Package</h4> <p style="font-size: 13px; color: #6b7684; margin: 0 0 8px 0;"> <strong>${envInfo.packageName}@latest</strong><br> Environment: <code>${envInfo.environment.toUpperCase()}</code> | Branch: <code>${envInfo.branchCode}</code> </p> <a href="#" id="kb-npmLink" target="_blank" style="font-size: 13px; color: #022d54; text-decoration: none; font-weight: 500; display: flex; align-items: center; gap: 4px;"> <span>View on NPM</span> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/> <polyline points="15 3 21 3 21 9"/> <line x1="10" y1="14" x2="21" y2="3"/> </svg> </a> </div> </div> <div style="padding: 20px; background: #fafbfc; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; gap: 16px;"> <div style="width: 48px; height: 48px; background: white; border: 1px solid #e1e7ef; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #022d54;"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> </svg> </div> <div style="flex: 1;"> <h4 style="font-size: 14px; font-weight: 600; color: #1a1f2c; margin: 0 0 4px 0;">Support</h4> <p style="font-size: 13px; color: #6b7684; margin: 0 0 8px 0;">Get help with integration and troubleshooting</p> <a href="/support/" style="font-size: 13px; color: #022d54; text-decoration: none; font-weight: 500; display: flex; align-items: center; gap: 4px;"> <span>Contact Support</span> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/> <polyline points="15 3 21 3 21 9"/> <line x1="10" y1="14" x2="21" y2="3"/> </svg> </a> </div> </div> </div> </div> </div> `; } /** * Setup event listeners for implementation tab * @param {Object} callbacks - Callback functions for events */ setupEventListeners(callbacks = {}) { // Development features toggle const devFeaturesToggle = this.widget.querySelector( '#kb-dev-features-toggle' ); const wordpressSection = this.widget.querySelector('#kb-wordpress-section'); if (devFeaturesToggle && wordpressSection) { // Check saved state from localStorage const savedToggleState = localStorage.getItem('kb-dev-features-enabled') === 'true'; devFeaturesToggle.checked = savedToggleState; wordpressSection.style.display = savedToggleState ? 'block' : 'none'; devFeaturesToggle.addEventListener('change', e => { const isEnabled = e.target.checked; wordpressSection.style.display = isEnabled ? 'block' : 'none'; localStorage.setItem('kb-dev-features-enabled', isEnabled.toString()); }); } // Copy buttons functionality - updated selectors const copyButtons = this.widget.querySelectorAll('.kb-copy-button'); copyButtons.forEach(button => { button.addEventListener('click', e => { e.preventDefault(); const targetId = button.getAttribute('data-copy-target'); const targetElement = this.widget.querySelector(`#${targetId} code`); if (targetElement) { const textToCopy = targetElement.textContent; this.copyToClipboard(textToCopy, button); } }); }); // NPM link - updated selector const npmLink = this.widget.querySelector('#kb-npmLink'); if (npmLink && callbacks.onNpmLinkClick) { npmLink.addEventListener('click', callbacks.onNpmLinkClick); } // CORS Management functionality - updated selectors this.setupCORSEventListeners(); } /** * Copy text to clipboard with visual feedback * @param {string} text - Text to copy * @param {HTMLElement} button - Button element for visual feedback */ copyToClipboard(text, button) { if (navigator.clipboard) { navigator.clipboard .writeText(text) .then(() => { this.showCopySuccess(button); }) .catch(() => { this.fallbackCopyTextToClipboard(text, button); }); } else { this.fallbackCopyTextToClipboard(text, button); } } /** * Fallback copy method for older browsers * @param {string} text - Text to copy * @param {HTMLElement} button - Button element for visual feedback */ fallbackCopyTextToClipboard(text, button) { const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.top = '0'; textArea.style.left = '0'; textArea.style.width = '2em'; textArea.style.height = '2em'; textArea.style.padding = '0'; textArea.style.border = 'none'; textArea.style.outline = 'none'; textArea.style.boxShadow = 'none'; textArea.style.background = 'transparent'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { const successful = document.execCommand('copy'); if (successful) { this.showCopySuccess(button); } else { this.showCopyError(button); } } catch (err) { this.showCopyError(button); } document.body.removeChild(textArea); } /** * Show copy success visual feedback * @param {HTMLElement} button - Button element */ showCopySuccess(button) { const originalText = button.querySelector('.kb-copy-text').textContent; const originalIcon = button.querySelector('svg').outerHTML; button.querySelector('.kb-copy-text').textContent = 'Copied!'; button.querySelector('svg').outerHTML = ` <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> `; button.style.background = '#16a34a'; button.style.borderColor = '#16a34a'; setTimeout(() => { button.querySelector('.kb-copy-text').textContent = originalText; button.querySelector('svg').outerHTML = originalIcon; button.style.background = '#022d54'; button.style.borderColor = '#022d54'; }, 2000); } /** * Show copy error visual feedback * @param {HTMLElement} button - Button element */ showCopyError(button) { const originalText = button.querySelector('.kb-copy-text').textContent; const originalBackground = button.style.background; const originalBorderColor = button.style.borderColor; button.querySelector('.kb-copy-text').textContent = 'Error'; button.style.background = '#dc2626'; button.style.borderColor = '#dc2626'; setTimeout(() => { button.querySelector('.kb-copy-text').textContent = originalText; button.style.background = originalBackground; button.style.borderColor = originalBorderColor; }, 2000); } /** * Extract environment and branch information from APIM endpoint * @returns {Object} Environment and branch information */ getEnvironmentInfo() { try { // First, try to get from bot management instance const botManagement = this.widget.botManagement || window.besperBotManagement; let apiEndpoint = null; if (botManagement) { // Try to get API endpoint from bot management options or environment apiEndpoint = botManagement.options?.apiEndpoint || botManagement.environment?.apiEndpoint || (typeof botManagement.getApiEndpoint === 'function' ? botManagement.getApiEndpoint() : null); } // Fallback to build-time environment if (!apiEndpoint) { apiEndpoint = process.env.API_ENDPOINT; } if (apiEndpoint) { console.log( '[API] Parsing environment info from API endpoint:', apiEndpoint ); // Parse the endpoint to extract environment and branch // Format: https://{environment}0926apim_internal.azure-api.net const match = apiEndpoint.match( /https:\/\/apim(dev|int|prod)(\d+)internal\.azure-api\.net/ ); if (match) { const [, environment, branchCode] = match; const packageName = `besper-frontend-toolkit-${environment}-${branchCode}`; console.log('[SUCCESS] Detected environment info:', { environment, branchCode, packageName, }); return { environment, branchCode, packageName, widgetEnvironment: environment, }; } } // Fallback: try to get from build-time environment variables const buildTimeEnv = process.env.NODE_ENV || 'development'; const buildTimePackage = process.env.PACKAGE_NAME || 'besper-frontend-toolkit'; console.log('[WARN] Using fallback environment info:', { buildTimeEnv, buildTimePackage, }); return { environment: buildTimeEnv === 'development' ? 'dev' : buildTimeEnv === 'integration' ? 'int' : 'prod', branchCode: 'unknown', packageName: buildTimePackage, widgetEnvironment: buildTimeEnv === 'development' ? 'dev' : buildTimeEnv === 'integration' ? 'int' : 'prod', }; } catch (error) { console.warn( '[WARN] Could not determine environment info, using defaults:', error ); return { environment: 'dev', branchCode: 'unknown', packageName: 'besper-frontend-toolkit-dev', widgetEnvironment: 'dev', }; } } /** * Generate implementation code for widget * @param {Object} config - Bot configuration * @returns {string} Widget implementation code */ generateWidgetCode(config) { if (!config) return 'Loading implementation code...'; const botId = config.bot_id || config.botId || 'YOUR_BOT_ID'; const envInfo = this.getEnvironmentInfo(); const packageVersion = 'latest'; // Always use @latest as requested return `<script type="module"> (async function() { const CHATBOT_ID = "${botId}"; const scriptUrl = "https://unpkg.com/${envInfo.packageName}@${packageVersion}/dist/besperbot.js"; try { // Import the module - this loads and executes the script await import(scriptUrl); // Small delay to ensure window assignment completes await new Promise(resolve => setTimeout(resolve, 100)); if (window.bsp_init_b_espersite) { const bot = await window.bsp_init_b_espersite(CHATBOT_ID, { environment: '${envInfo.widgetEnvironment}', position: 'bottom-right' }); console.log('Chat widget initialized successfully'); } else if (window.BesperBot && window.BesperBot.bsp_init_b_espersite) { const bot = await window.BesperBot.bsp_init_b_espersite(CHATBOT_ID, { environment: '${envInfo.widgetEnvironment}', position: 'bottom-right' }); console.log('Chat widget initialized successfully'); } else { console.error('Function not found on window after import'); console.log('Available on window:', Object.keys(window).filter(k => k.includes('esper'))); console.log('Window.BesperBot:', window.BesperBot); } } catch (error) { console.error('Failed to initialize chat widget:', error); } })(); </script>`; } /** * Generate implementation code for container * @param {Object} config - Bot configuration * @returns {string} Container implementation code */ generateContainerCode(config) { if (!config) return 'Loading implementation code...'; const botId = config.bot_id || config.botId || 'YOUR_BOT_ID'; const envInfo = this.getEnvironmentInfo(); const packageVersion = 'latest'; // Always use @latest as requested return `<!-- Chat Container --> <div class="chat-container" id="chatContainer" style="width: 100%; height: 600px; border: 1px solid #e1e5e9; border-radius: 8px; overflow: hidden;"> <!-- Chat widget will be embedded here --> </div> <!-- B-esper Chat Widget Implementation --> <script type="module"> (async function() { const CHATBOT_ID = "${botId}"; const scriptUrl = "https://unpkg.com/${envInfo.packageName}@${packageVersion}/dist/besperbot.js"; try { // Import the module - this loads and executes the script await import(scriptUrl); // Small delay to ensure window assignment completes await new Promise(resolve => setTimeout(resolve, 100)); if (window.bsp_init_b_espersite) { const bot = await window.bsp_init_b_espersite(CHATBOT_ID, { environment: '${envInfo.widgetEnvironment}', container: '#chatContainer' }); console.log('Chat widget initialized successfully'); } else if (window.BesperBot && window.BesperBot.bsp_init_b_espersite) { const bot = await window.BesperBot.bsp_init_b_espersite(CHATBOT_ID, { environment: '${envInfo.widgetEnvironment}', container: '#chatContainer' }); console.log('Chat widget initialized successfully'); } else { console.error('Function not found on window after import'); console.log('Available on window:', Object.keys(window).filter(k => k.includes('esper'))); console.log('Window.BesperBot:', window.BesperBot); } } catch (error) { console.error('Failed to initialize chat widget:', error); } })(); </script>`; } /** * Generate WordPress integration code * @param {Object} config - Bot configuration * @returns {string} WordPress integration code */ generateWordPressCode(config) { if (!config) return 'Loading WordPress integration code...'; const botId = config.bot_id || config.botId || 'YOUR_BOT_ID'; const envInfo = this.getEnvironmentInfo(); const packageVersion = 'latest'; // Always use @latest as requested return `<?php /** * Add B-esper Chat Widget to WordPress * Add this code to your theme's functions.php file */ // Enqueue the B-esper chat widget script function besper_chat_widget_scripts() { // Only load on frontend if (!is_admin()) { wp_enqueue_script( 'besper-chat-widget', 'https://unpkg.com/${envInfo.packageName}@${packageVersion}/dist/besperbot.js', array(), '${packageVersion}', true ); // Add the chat widget initialization wp_add_inline_script('besper-chat-widget', ' const CHATBOT_ID = "${botId}"; (async () => { try { // Wait for script to load await new Promise(resolve => setTimeout(resolve, 100)); if (window.bsp_init_b_espersite) { const bot = await window.bsp_init_b_espersite(CHATBOT_ID, { environment: "${envInfo.widgetEnvironment}", position: "bottom-right" }); console.log("Chat widget initialized successfully"); } else if (window.BesperBot && window.BesperBot.bsp_init_b_espersite) { const bot = await window.BesperBot.bsp_init_b_espersite(CHATBOT_ID, { environment: "${envInfo.widgetEnvironment}", position: "bottom-right" }); console.log("Chat widget initialized successfully"); } else { console.error("Function not found on window after import"); console.log("Available on window:", Object.keys(window).filter(k => k.includes("esper"))); console.log("Window.BesperBot:", window.BesperBot); } } catch (error) { console.error("Failed to initialize chat widget:", error); } })(); '); } } add_action('wp_enqueue_scripts', 'besper_chat_widget_scripts'); /** * Optional: Add shortcode support for embedded chat * Usage: [besper_chat_container height="600px"] */ function besper_chat_container_shortcode($atts) { $atts = shortcode_atts(array( 'height' => '600px', 'width' => '100%', 'id' => 'besper-chat-container-' . uniqid() ), $atts, 'besper_chat_container'); ob_start(); ?> <div id="<?php echo esc_attr($atts['id']); ?>" style="width: <?php echo esc_attr($atts['width']); ?>; height: <?php echo esc_attr($atts['height']); ?>; border: 1px solid #e1e5e9; border-radius: 8px; overflow: hidden;"></div> <script type="module"> (async () => { const CHATBOT_ID = "${botId}"; const scriptUrl = "https://unpkg.com/${envInfo.packageName}@${packageVersion}/dist/besperbot.js"; try { await import(scriptUrl); await new Promise(resolve => setTimeout(resolve, 100)); if (window.bsp_init_b_espersite) { const bot = await window.bsp_init_b_espersite(CHATBOT_ID, { environment: "${envInfo.widgetEnvironment}", container: "#<?php echo esc_attr($atts['id']); ?>" }); console.log("Chat widget initialized successfully"); } else if (window.BesperBot && window.BesperBot.bsp_init_b_espersite) { const bot = await window.BesperBot.bsp_init_b_espersite(CHATBOT_ID, { environment: "${envInfo.widgetEnvironment}", container: "#<?php echo esc_attr($atts['id']); ?>" }); console.log("Chat widget initialized successfully"); } else { console.error("Function not found on window after import"); console.log("Available on window:", Object.keys(window).filter(k => k.includes("esper"))); console.log("Window.BesperBot:", window.BesperBot); } } catch (error) { console.error("Failed to initialize chat widget:", error); } })(); </script> <?php return ob_get_clean(); } add_shortcode('besper_chat_container', 'besper_chat_container_shortcode'); ?>`; } /** * Update implementation code * @param {Object} config - Bot configuration */ updateImplementationCode(config) { console.log('[LOADING] Updating implementation code with config:', config); // First, check if we need to regenerate the tab content const implementationTab = this.widget.querySelector( '#bm-tab-implementation' ); if (!implementationTab) { console.error('[ERROR] Implementation tab container not found in DOM'); return; } console.log('[SUCCESS] Implementation tab container found'); const widgetCodeElement = this.widget.querySelector('#kb-widgetCode code'); const containerCodeElement = this.widget.querySelector( '#kb-containerCode code' ); const wordpressCodeElement = this.widget.querySelector( '#kb-wordpressCode code' ); console.log('🔍 Code elements found:', { widgetCodeElement: !!widgetCodeElement, containerCodeElement: !!containerCodeElement, wordpressCodeElement: !!wordpressCodeElement, implementationTabHTML: implementationTab.innerHTML.substring(0, 200) + '...', }); // If elements are not found, regenerate the tab content if (!widgetCodeElement || !containerCodeElement || !wordpressCodeElement) { console.log('🔧 Code elements missing, regenerating tab content...'); // Extract the inner content and update the tab const fullHTML = this.getHTML(); const match = fullHTML.match( /<div class="bm-tab-content"[^>]*>([\s\S]*)<\/div>\s*$/ ); const innerContent = match ? match[1] : fullHTML; implementationTab.innerHTML = innerContent; console.log('[LOADING] Tab content regenerated'); // Re-setup event listeners for the new content this.setupEventListeners(); console.log('🔧 Event listeners re-initialized'); // Try to find elements again const newWidgetCodeElement = this.widget.querySelector( '#kb-widgetCode code' ); const newContainerCodeElement = this.widget.querySelector( '#kb-containerCode code' ); const newWordpressCodeElement = this.widget.querySelector( '#kb-wordpressCode code' ); console.log('🔍 After regeneration, elements found:', { newWidgetCodeElement: !!newWidgetCodeElement, newContainerCodeElement: !!newContainerCodeElement, newWordpressCodeElement: !!newWordpressCodeElement, }); if (newWidgetCodeElement) { const widgetCode = this.generateWidgetCode(config); console.log( '🔧 Generated widget code (first 100 chars):', widgetCode.substring(0, 100) ); newWidgetCodeElement.textContent = widgetCode; } if (newContainerCodeElement) { const containerCode = this.generateContainerCode(config); console.log( '🔧 Generated container code (first 100 chars):', containerCode.substring(0, 100) ); newContainerCodeElement.textContent = containerCode; } if (newWordpressCodeElement) { const wordpressCode = this.generateWordPressCode(config); console.log( '🔧 Generated WordPress code (first 100 chars):', wordpressCode.substring(0, 100) ); newWordpressCodeElement.textContent = wordpressCode; } } else { // Elements exist, update them directly if (widgetCodeElement) { const widgetCode = this.generateWidgetCode(config); console.log( '🔧 Generated widget code (first 100 chars):', widgetCode.substring(0, 100) ); widgetCodeElement.textContent = widgetCode; } if (containerC