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
JavaScript
/**
* 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