diff --git a/.cursor/rules/development.mdc b/.cursor/rules/development.mdc
new file mode 100644
index 0000000..0f0e866
--- /dev/null
+++ b/.cursor/rules/development.mdc
@@ -0,0 +1,61 @@
+---
+description: General rules for WordPress Development
+globs:
+alwaysApply: true
+---
+
+You are an expert in WordPress, PHP, PHP Sniffer, coding standard and related web development technologies.
+
+Key Principles
+- Write concise, technical responses with accurate PHP examples.
+- Follow WordPress coding standards and best practices.
+- Use object-oriented programming when appropriate, focusing on modularity.
+- Prefer iteration and modularization over duplication.
+- Use descriptive function, variable, and file names.
+
+Format code
+- Use tabs for indentations.
+- Space to align equals in same group of variable definitions.
+- Always write code and comments in English.
+- Documentation always add in /docs and add it to .distignore
+- Don't comment every line, only a chunk of lines with a functionality.
+- Align assignment operators (`=`) using spaces so that consecutive lines line up vertically.
+- Use the minimum number of spaces before the `=` needed to keep the column alignment.
+- Keep exactly one space on each side of the operator (`Squiz.WhiteSpace.OperatorSpacing`).
+
+PHP/WordPress
+- Use PHP 7.4+ features when appropriate (e.g., typed properties, arrow functions).
+- Follow WordPress PHP Coding Standards.
+- Utilize WordPress core functions and APIs when available.
+- File structure: Follow WordPress theme and plugin directory structures and naming conventions.
+- Implement proper error handling and logging.
+- Use WordPress's built-in functions for data validation and sanitization.
+- Use prepare() statements for secure database queries.
+- Use Yoda conditions ALWAYS.
+- PHP inline comments must start with capital letter and end with period character.
+- PHP inline comments should be concise.
+
+JavaScript
+- Don't use jQuery. Better Vanilla JavaScript.
+
+Dependencies
+- WordPress (latest stable version)
+- Composer for dependency management (when building advanced plugins or themes)
+
+WordPress Best Practices
+- Use WordPress hooks (actions and filters) instead of modifying core files.
+- Implement proper theme functions using functions.php.
+- Use WordPress's built-in user roles and capabilities system.
+- Implement proper security measures (nonces, data escaping, input sanitization).
+
+Key Conventions
+1. Follow WordPress's plugin API for extending functionality.
+2. Use WordPress's template hierarchy for theme development.
+3. Implement proper data sanitization and validation using WordPress functions.
+4. Use WordPress's template tags and conditional tags in themes.
+5. Implement proper database queries using $wpdb or WP_Query.
+6. Use WordPress's authentication and authorization functions.
+7. Implement proper AJAX handling using admin-ajax.php or REST API.
+8. Use WordPress's hook system for modular and extensible code.
+9. Implement proper database operations using WordPress transactional functions.
+10. Use WordPress's WP_Cron API for scheduling tasks.
\ No newline at end of file
diff --git a/.distignore b/.distignore
new file mode 100644
index 0000000..38aa1c8
--- /dev/null
+++ b/.distignore
@@ -0,0 +1,19 @@
+.distignore
+.editorconfig
+.git
+.gitignore
+.travis.yml
+circle.yml
+.DS_Store
+.github
+.cursor
+composer.lock
+.wordpress-org
+*.sql
+*.tar.gz
+*.zip
+phpstan.neon.dist
+.phpcs.xml.dist
+.phplint.yml
+tests/
+languages/
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..02bd6d3
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,18 @@
+name: Deploy to WordPress.org
+on:
+ push:
+ tags:
+ - "*"
+jobs:
+ tag:
+ name: New tag
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@master
+ - run: "composer install --no-dev"
+ - name: WordPress Plugin Deploy
+ uses: 10up/action-wordpress-plugin-deploy@stable
+ env:
+ SVN_PASSWORD: ${{ secrets.SVN_PASSWORD_CTECH }}
+ SVN_USERNAME: ${{ secrets.SVN_USERNAME_CTECH }}
\ No newline at end of file
diff --git a/.github/workflows/php-lint.yml b/.github/workflows/php-lint.yml
new file mode 100644
index 0000000..8917772
--- /dev/null
+++ b/.github/workflows/php-lint.yml
@@ -0,0 +1,65 @@
+name: PHP Code Linting
+
+on:
+ push:
+ branches:
+ - main
+ - 'release/**'
+ # Only run if PHP-related files changed.
+ paths:
+ - '.github/workflows/php-lint.yml'
+ - '**.php'
+ - '.phpcs.xml.dist'
+ - 'phpstan.neon.dist'
+ - 'composer.json'
+ - 'composer.lock'
+ pull_request:
+ branches:
+ - main
+ - 'release/**'
+ - 'feature/**'
+ # Only run if PHP-related files changed.
+ paths:
+ - '.github/workflows/php-lint.yml'
+ - '**.php'
+ - '.phpcs.xml.dist'
+ - 'phpstan.neon.dist'
+ - 'composer.json'
+ - 'composer.lock'
+ types:
+ - opened
+ - reopened
+ - synchronize
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.ref != 'refs/heads/trunk' }}
+jobs:
+ php-lint:
+ name: PHP
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3'
+
+ - name: Validate Composer configuration
+ run: composer validate
+
+ - name: Install PHP dependencies
+ uses: ramsey/composer-install@a2636af0004d1c0499ffca16ac0b4cc94df70565
+ with:
+ composer-options: '--prefer-dist'
+
+ - name: PHP Lint
+ run: composer lint
+
+ - name: PHP PHPStan
+ run: composer phpstan
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5f838fa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+# WordPress
+vendor/
+node_modules/
+
+# Numerous always-ignore extensions
+*.log
+*.zip
+.DS_Store
+
+# OS or Editor folders
+._*.cache
+
diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist
new file mode 100644
index 0000000..ea8fe35
--- /dev/null
+++ b/.phpcs.xml.dist
@@ -0,0 +1,47 @@
+
+');
+ removeBtn.show();
+ });
+
+ // Open the modal.
+ fileFrame.open();
+ });
+
+ // Handle icon remove button.
+ $(document).on('click', '.knowledge-base-chatbot-remove-icon', function(e) {
+ e.preventDefault();
+
+ const button = $(this);
+ const fieldId = button.data('field-id');
+ const fileInput = $('#' + fieldId);
+ const preview = button.closest('.knowledge-base-chatbot-icon-field').find('.knowledge-base-chatbot-icon-preview');
+
+ fileInput.val('');
+ preview.html('
' + (i18n.noIconSelected || 'No icon selected. Default icon will be used.') + '
'); + button.hide(); + }); + }); +})(jQuery); + diff --git a/assets/generate.css b/assets/generate.css new file mode 100644 index 0000000..d2b530f --- /dev/null +++ b/assets/generate.css @@ -0,0 +1,306 @@ +/* Generate Page Styles */ + +.kbcb-generate-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 30px; + margin-top: 30px; +} + +@media (max-width: 1200px) { + .kbcb-generate-container { + grid-template-columns: 1fr; + } +} + +/* Selection Section */ +.kbcb-selection-section { + background: #fff; + padding: 20px; + border: 1px solid #ccd0d4; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); +} + +.kbcb-selection-section h2 { + margin-top: 0; + margin-bottom: 20px; + font-size: 18px; +} + +/* Tabs */ +.kbcb-tabs .nav-tab-wrapper { + margin-bottom: 0; + border-bottom: 1px solid #ccd0d4; +} + +.kbcb-tab-content { + display: none; + padding: 20px 0; +} + +.kbcb-tab-content.kbcb-tab-active { + display: block; +} + +/* Items List */ +.kbcb-items-list { + max-height: 400px; + overflow-y: auto; + border: 1px solid #ddd; + padding: 10px; + margin-bottom: 15px; + background: #f9f9f9; +} + +.kbcb-item { + display: flex; + align-items: center; + padding: 12px; + margin-bottom: 5px; + background: #fff; + border: 1px solid #e5e5e5; + border-radius: 3px; + cursor: pointer; + transition: all 0.2s; +} + +.kbcb-item:hover { + background: #f0f0f0; + border-color: #0073aa; +} + +.kbcb-item.kbcb-item-selected { + background: #e8f5e9; + border-color: #4caf50; +} + +.kbcb-item input[type="checkbox"] { + margin: 0 10px 0 0; + flex-shrink: 0; +} + +.kbcb-item-title { + font-weight: 600; + color: #23282d; + flex: 1; +} + +.kbcb-item-type { + font-size: 12px; + color: #666; + padding: 2px 8px; + background: #f0f0f0; + border-radius: 3px; + margin-left: 10px; +} + +/* Controls */ +.kbcb-controls { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +/* Selected Section */ +.kbcb-selected-section { + background: #fff; + padding: 20px; + border: 1px solid #ccd0d4; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); +} + +.kbcb-selected-section h2 { + margin-top: 0; + margin-bottom: 10px; + font-size: 18px; +} + +.kbcb-selected-section > .description { + margin-bottom: 20px; + color: #666; +} + +/* Selected List */ +.kbcb-selected-list { + min-height: 200px; + max-height: 500px; + overflow-y: auto; + border: 1px solid #ddd; + padding: 10px; + margin-bottom: 20px; + background: #f9f9f9; +} + +.kbcb-empty-message { + text-align: center; + color: #666; + padding: 40px 20px; + font-style: italic; +} + +.kbcb-selected-item { + display: flex; + align-items: center; + padding: 12px; + margin-bottom: 8px; + background: #fff; + border: 1px solid #e5e5e5; + border-radius: 3px; + transition: all 0.2s; +} + +.kbcb-selected-item:hover { + border-color: #0073aa; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.kbcb-drag-handle { + cursor: move; + color: #999; + margin-right: 12px; + font-size: 18px; +} + +.kbcb-drag-handle:hover { + color: #0073aa; +} + +.kbcb-selected-item-info { + flex: 1; + display: flex; + flex-direction: column; +} + +.kbcb-selected-item-title { + font-weight: 600; + color: #23282d; + margin-bottom: 4px; +} + +.kbcb-selected-item-type { + font-size: 12px; + color: #666; +} + +.wp-core-ui .button.kbcb-remove-item { + padding: 10px 8px 0; + min-width: auto; + height: auto; + background: transparent; + border: none; + color: #dc3232; + cursor: pointer; + transition: all 0.2s; +} + +.kbcb-remove-item:hover { + color: #a00; + background: #ffe5e5; +} + +.kbcb-remove-item .dashicons { + font-size: 18px; + width: 18px; + height: 18px; +} + +/* Sortable Placeholder */ +.kbcb-selected-item-placeholder { + background: #e3f2fd; + border: 2px dashed #2196f3; + margin-bottom: 8px; + height: 50px; + border-radius: 3px; +} + +/* Actions */ +.kbcb-actions { + display: flex; + gap: 10px; + margin-bottom: 20px; + flex-wrap: wrap; +} + +/* File Info */ +.kbcb-file-info { + background: #f0f6fc; + border: 1px solid #c3e0f7; + border-radius: 4px; + padding: 15px; + margin-bottom: 20px; +} + +.kbcb-file-info h3 { + margin: 0 0 10px 0; + font-size: 14px; + color: #0073aa; +} + +.kbcb-file-url { + display: flex; + gap: 10px; + margin-bottom: 10px; +} + +.kbcb-file-url input[type="text"] { + flex: 1; + padding: 8px 12px; + border: 1px solid #ccd0d4; + border-radius: 3px; + background: #fff; + font-family: monospace; + font-size: 13px; +} + +.wp-core-ui .button.kbcb-copy-url { + padding: 12px 12px 0; + min-width: auto; +} + +.wp-core-ui .button.kbcb-copy-url .dashicons { + font-size: 16px; + width: 16px; + height: 16px; +} + +.kbcb-file-date { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: rgba(0, 115, 170, 0.05); + border-radius: 3px; + font-size: 13px; +} + +.kbcb-file-date .dashicons { + color: #0073aa; + font-size: 16px; + width: 16px; + height: 16px; +} + +.kbcb-file-date strong { + color: #23282d; +} + +.kbcb-file-date-utc { + color: #666; + font-size: 11px; + text-transform: uppercase; +} + +/* Message */ +.kbcb-message { + margin-top: 15px; +} + +.kbcb-message p { + margin: 0.5em 0; +} + +/* Loading States */ +button[disabled] { + opacity: 0.6; + cursor: not-allowed !important; +} diff --git a/assets/generate.js b/assets/generate.js new file mode 100644 index 0000000..6ec908b --- /dev/null +++ b/assets/generate.js @@ -0,0 +1,323 @@ +jQuery(document).ready(function ($) { + const i18n = (typeof kbcbGenerate !== 'undefined' && kbcbGenerate.i18n) ? kbcbGenerate.i18n : {}; + + // Tab switching + $('.nav-tab-wrapper .nav-tab').on('click', function (e) { + e.preventDefault(); + const tab = $(this).data('tab'); + + // Update tab navigation. + $('.nav-tab-wrapper .nav-tab').removeClass('nav-tab-active'); + $(this).addClass('nav-tab-active'); + + // Update tab content. + $('.kbcb-tab-content').removeClass('kbcb-tab-active'); + $('#kbcb-tab-' + tab).addClass('kbcb-tab-active'); + }); + + // Select all items. + $('.kbcb-select-all').on('click', function () { + const type = $(this).data('type'); + $('.kbcb-item-checkbox[data-type="' + type + '"]').not(':disabled').prop('checked', true); + }); + + // Deselect all items. + $('.kbcb-deselect-all').on('click', function () { + const type = $(this).data('type'); + $('.kbcb-item-checkbox[data-type="' + type + '"]').prop('checked', false); + }); + + // Add selected items. + $('.kbcb-add-selected').on('click', function () { + const type = $(this).data('type'); + const selectedItems = $('.kbcb-item-checkbox[data-type="' + type + '"]:checked') + .map(function () { + return $(this).val(); + }) + .get(); + + if (selectedItems.length === 0) { + showMessage(i18n.selectAtLeastOne || 'Please select at least one item.', 'error'); + return; + } + + addPages(selectedItems); + }); + + // Remove item from selected list. + $(document).on('click', '.kbcb-remove-item', function () { + const postId = $(this).data('id'); + removePage(postId); + }); + + // Save order button. + $('#kbcb-save-order').on('click', function () { + saveOrder(); + }); + + // Regenerate button. + $('#kbcb-regenerate').on('click', function () { + regenerateFile(); + }); + + // Copy URL button. + $(document).on('click', '.kbcb-copy-url', function () { + const $input = $(this).prev('input'); + $input.select(); + document.execCommand('copy'); + showMessage(i18n.urlCopied || 'URL copied to clipboard.', 'success'); + }); + + // Make selected list sortable. + if ($('#kbcb-selected-list').length) { + $('#kbcb-selected-list').sortable({ + handle: '.kbcb-drag-handle', + placeholder: 'kbcb-selected-item-placeholder', + update: function () { + // Optional: Auto-save on reorder. + // saveOrder(); + } + }); + } + + /** + * Add pages to selected list + * + * @param {Array} postIds Array of post IDs. + */ + function addPages(postIds) { + const $button = $('.kbcb-add-selected'); + const originalText = $button.text(); + $button.prop('disabled', true).text(i18n.adding || 'Adding...'); + + $.ajax({ + url: kbcbGenerate.ajaxUrl, + type: 'POST', + data: { + action: 'kbcb_add_pages', + nonce: kbcbGenerate.nonce, + post_ids: postIds + }, + success: function (response) { + if (response.success) { + showMessage(response.data.message, 'success'); + + // Update file info if provided. + updateFileInfo(response.data.fileUrl, response.data.fileDate); + + // Reload page to update selected list. + setTimeout(function() { + location.reload(); + }, 1000); + } else { + showMessage(response.data.message || (i18n.addError || 'Error adding items.'), 'error'); + } + }, + error: function () { + showMessage(i18n.serverError || 'Communication error with the server.', 'error'); + }, + complete: function () { + $button.prop('disabled', false).text(originalText); + } + }); + } + + /** + * Remove page from selected list + * + * @param {number} postId Post ID. + */ + function removePage(postId) { + if (!confirm(i18n.removeConfirm || 'Are you sure you want to remove this item from the list?')) { + return; + } + + $.ajax({ + url: kbcbGenerate.ajaxUrl, + type: 'POST', + data: { + action: 'kbcb_remove_page', + nonce: kbcbGenerate.nonce, + post_id: postId + }, + success: function (response) { + if (response.success) { + // Remove item from DOM. + $('.kbcb-selected-item[data-id="' + postId + '"]').fadeOut(function () { + $(this).remove(); + + // Show empty message if no items left. + if ($('.kbcb-selected-item').length === 0) { + $('#kbcb-selected-list').html(''); + } + }); + + // Uncheck checkbox if exists. + $('.kbcb-item-checkbox[value="' + postId + '"]').prop('checked', false).closest('.kbcb-item').removeClass('kbcb-item-selected'); + + // Update file info. + updateFileInfo(response.data.fileUrl, response.data.fileDate); + + showMessage(response.data.message, 'success'); + } else { + showMessage(response.data.message || (i18n.removeError || 'Error removing item.'), 'error'); + } + }, + error: function () { + showMessage(i18n.serverError || 'Communication error with the server.', 'error'); + } + }); + } + + /** + * Save order of selected pages + */ + function saveOrder() { + const order = []; + $('.kbcb-selected-item').each(function () { + order.push($(this).data('id')); + }); + + if (order.length === 0) { + showMessage(i18n.noItemsToSave || 'There are no items to save.', 'error'); + return; + } + + const $button = $('#kbcb-save-order'); + const originalText = $button.text(); + $button.prop('disabled', true).text(i18n.saving || 'Saving...'); + + $.ajax({ + url: kbcbGenerate.ajaxUrl, + type: 'POST', + data: { + action: 'kbcb_save_order', + nonce: kbcbGenerate.nonce, + order: order + }, + success: function (response) { + if (response.success) { + showMessage(response.data.message, 'success'); + } else { + showMessage(response.data.message || (i18n.saveOrderError || 'Error saving order.'), 'error'); + } + }, + error: function () { + showMessage(i18n.serverError || 'Communication error with the server.', 'error'); + }, + complete: function () { + $button.prop('disabled', false).text(originalText); + } + }); + } + + /** + * Regenerate markdown file + */ + function regenerateFile() { + if (!confirm(i18n.regenerateConfirm || 'Are you sure you want to regenerate the file? This will overwrite the existing file.')) { + return; + } + + const $button = $('#kbcb-regenerate'); + const originalText = $button.text(); + $button.prop('disabled', true).text(i18n.generating || 'Generating...'); + + $.ajax({ + url: kbcbGenerate.ajaxUrl, + type: 'POST', + data: { + action: 'kbcb_regenerate', + nonce: kbcbGenerate.nonce + }, + success: function (response) { + if (response.success) { + showMessage(response.data.message, 'success'); + + // Update file info. + updateFileInfo(response.data.fileUrl, response.data.fileDate); + } else { + showMessage(response.data.message || (i18n.generateFileError || 'Error generating file.'), 'error'); + } + }, + error: function () { + showMessage(i18n.serverError || 'Communication error with the server.', 'error'); + }, + complete: function () { + $button.prop('disabled', false).text(originalText); + } + }); + } + + /** + * Update file info + * + * @param {string} fileUrl File URL. + * @param {string} fileDate File date. + */ + function updateFileInfo(fileUrl, fileDate) { + if (!fileUrl) { + return; + } + + // Update or create file info section. + if ($('.kbcb-file-info').length) { + $('.kbcb-file-url input').val(fileUrl); + if (fileDate && $('#kbcb-file-date-value').length) { + $('#kbcb-file-date-value').text(fileDate); + } else if (fileDate) { + // Add date if it doesn't exist. + $('.kbcb-file-url').after(` +' + message + '
') + .show(); + + setTimeout(function () { + $message.fadeOut(); + }, 5000); + } +}); + diff --git a/assets/knowledge-base-chatbot-admin.css b/assets/knowledge-base-chatbot-admin.css new file mode 100644 index 0000000..46cde01 --- /dev/null +++ b/assets/knowledge-base-chatbot-admin.css @@ -0,0 +1,144 @@ +.knowledge-base-chatbot-export-tabs { + margin-top: 30px; +} + +.knowledge-base-chatbot-export-tabs .nav-tab-wrapper { + margin-bottom: 0; + border-bottom: 1px solid #ccd0d4; +} + +.knowledge-base-chatbot-tab-content { + display: none; + padding: 20px; + background: #fff; + border: 1px solid #ccd0d4; + border-top: none; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); +} + +.knowledge-base-chatbot-tab-content.knowledge-base-chatbot-tab-active { + display: block; +} + +.knowledge-base-chatbot-export-section { + margin-top: 0; + padding: 0; + background: transparent; + border: none; + box-shadow: none; +} + +.knowledge-base-chatbot-export-controls { + margin-bottom: 20px; +} + +.knowledge-base-chatbot-export-controls .button { + margin-right: 10px; +} + +.knowledge-base-chatbot-items-list { + max-height: 400px; + overflow-y: auto; + border: 1px solid #ddd; + padding: 10px; + margin-bottom: 20px; + background: #f9f9f9; +} + +.knowledge-base-chatbot-item { + display: block; + padding: 10px; + margin-bottom: 5px; + background: #fff; + border: 1px solid #e5e5e5; + border-radius: 3px; + cursor: pointer; + transition: background-color 0.2s; +} + +.knowledge-base-chatbot-item:hover { + background: #f0f0f0; +} + +.knowledge-base-chatbot-item input[type="checkbox"] { + margin-right: 10px; +} + +.knowledge-base-chatbot-item-title { + font-weight: 600; + color: #23282d; + display: block; + margin-bottom: 5px; +} + +.knowledge-base-chatbot-item-url { + font-size: 12px; + color: #666; + display: block; +} + +.knowledge-base-chatbot-export-buttons { + margin-top: 20px; +} + +.knowledge-base-chatbot-export-buttons .button { + margin-right: 10px; +} + +.knowledge-base-chatbot-export-message { + margin-top: 15px; +} + +.knowledge-base-chatbot-export-message p { + margin: 0.5em 0; +} + +/* Legacy support for old class names */ +.knowledge-base-chatbot-pages-list { + max-height: 400px; + overflow-y: auto; + border: 1px solid #ddd; + padding: 10px; + margin-bottom: 20px; + background: #f9f9f9; +} + +.knowledge-base-chatbot-page-item { + display: block; + padding: 10px; + margin-bottom: 5px; + background: #fff; + border: 1px solid #e5e5e5; + border-radius: 3px; + cursor: pointer; + transition: background-color 0.2s; +} + +.knowledge-base-chatbot-page-item:hover { + background: #f0f0f0; +} + +.knowledge-base-chatbot-page-item input[type="checkbox"] { + margin-right: 10px; +} + +.knowledge-base-chatbot-page-title { + font-weight: 600; + color: #23282d; + display: block; + margin-bottom: 5px; +} + +.knowledge-base-chatbot-page-url { + font-size: 12px; + color: #666; + display: block; +} + +.knowledge-base-chatbot-message { + margin-top: 15px; +} + +.knowledge-base-chatbot-message p { + margin: 0.5em 0; +} diff --git a/assets/knowledge-base-chatbot-admin.js b/assets/knowledge-base-chatbot-admin.js new file mode 100644 index 0000000..d3980d1 --- /dev/null +++ b/assets/knowledge-base-chatbot-admin.js @@ -0,0 +1,194 @@ +jQuery(document).ready(function ($) { + const i18n = (typeof knowledgeBaseChatbotAdmin !== 'undefined' && knowledgeBaseChatbotAdmin.i18n) ? knowledgeBaseChatbotAdmin.i18n : {}; + + // Tab switching + $('.nav-tab-wrapper .nav-tab').on('click', function (e) { + e.preventDefault(); + const tab = $(this).data('tab'); + + // Update tab navigation + $('.nav-tab-wrapper .nav-tab').removeClass('nav-tab-active'); + $(this).addClass('nav-tab-active'); + + // Update tab content + $('.knowledge-base-chatbot-tab-content').removeClass('knowledge-base-chatbot-tab-active'); + $('#knowledge-base-chatbot-tab-' + tab).addClass('knowledge-base-chatbot-tab-active'); + }); + + // Select all items (pages or posts) + $('.knowledge-base-chatbot-select-all').on('click', function () { + const type = $(this).data('type'); + $('.knowledge-base-chatbot-item-checkbox[data-type="' + type + '"]').prop('checked', true); + }); + + // Deselect all items (pages or posts) + $('.knowledge-base-chatbot-deselect-all').on('click', function () { + const type = $(this).data('type'); + $('.knowledge-base-chatbot-item-checkbox[data-type="' + type + '"]').prop('checked', false); + }); + + // Export selected items (any post type) + $('.knowledge-base-chatbot-export-selected').on('click', function () { + const type = $(this).data('type'); + const selectedItems = $('.knowledge-base-chatbot-item-checkbox[data-type="' + type + '"]:checked') + .map(function () { + return $(this).val(); + }) + .get(); + + if (selectedItems.length === 0) { + showMessage( + i18n.selectAtLeastOneToExport || 'Please select at least one item to export.', + 'error', + type + ); + return; + } + + exportSelected(type, selectedItems); + }); + + // Export all items (any post type) + $('.knowledge-base-chatbot-export-all').on('click', function () { + const type = $(this).data('type'); + + if ( + !confirm( + i18n.confirmExportAll || 'Are you sure you want to export all items of this type?' + ) + ) { + return; + } + + exportAll(type); + }); + + /** + * Export selected items (generic for any post type) + * + * @param {string} postType Post type. + * @param {Array} postIds Array of post IDs. + */ + function exportSelected(postType, postIds) { + const $button = $('.knowledge-base-chatbot-export-selected[data-type="' + postType + '"]'); + const originalText = $button.text(); + $button.prop('disabled', true).text(i18n.exporting || 'Exporting...'); + + // Create form and submit + const form = $('