Showing 1-12 of 247 articles
Orders
4.8

How to Process Order Cancellations

Step-by-step guide for handling customer order cancellation requests, including refund processing and inventory updates...

Popularity Very High
1,247 views Updated 2h ago
Payment
4.6

Troubleshooting Payment Gateway Errors

Common payment failures, error codes, and resolution steps for various payment methods including cards and digital wallets...

Popularity High
892 views Updated 1d ago
Delivery
4.9

Delivery Address Change Requests

Handle customer requests for delivery address changes after order placement, including carrier coordination...

Popularity Medium
634 views Updated 3d ago
Product
4.7

Product Quality Complaints

Best practices for handling product quality issues, return processes, and vendor communication...

Popularity Medium
445 views Updated 5d ago
4.5

Account Recovery & Password Reset

Guide customers through account recovery process when they can't access their accounts...

Popularity High
723 views Updated 1w ago
Technical
4.3

Mobile App Troubleshooting

Common mobile app issues, crash reports, and step-by-step resolution for iOS and Android...

Popularity Low
234 views Updated 2w ago
`); printWindow.document.close(); printWindow.print(); } } // ===================================================================================== // UI RENDERING FUNCTIONS // ===================================================================================== function renderArticles(data, highlightQuery = null) { const articlesGrid = document.getElementById('articlesGrid'); if (!articlesGrid) return; const articles = data.items || data; if (!articles || articles.length === 0) { articlesGrid.innerHTML = `

No Articles Found

Try adjusting your search or filters

`; return; } articlesGrid.innerHTML = articles.map(article => { const categoryClass = getCategoryClass(article.category_name || article.tags?.[0] || 'general'); const popularity = calculatePopularity(article.view_count, article.helpful_count); const title = highlightQuery ? highlightText(article.title, highlightQuery) : article.title; const excerpt = highlightQuery ? highlightText(article.excerpt || '', highlightQuery) : (article.excerpt || ''); return `
${article.category_name || 'General'}
${calculateRating(article)}

${title}

${excerpt.substring(0, 150)}...

Popularity ${popularity.label}
${formatNumber(article.view_count || 0)} views ${formatTimeAgo(article.updated_at || article.created_at)}
`; }).join(''); // Update pagination info updatePaginationInfo(data.total, currentPage, perPage); } function renderCategories(categories) { // Update sidebar categories with actual counts if (!categories || !Array.isArray(categories)) return; categories.forEach(cat => { const btn = document.querySelector(`[onclick="filterByCategory('${cat.slug}')"]`); if (btn) { const countSpan = btn.querySelector('span:last-child'); if (countSpan) { countSpan.textContent = cat.article_count || 0; } } }); } function renderFAQs(faqs) { const faqsTab = document.getElementById('faqsTab'); if (!faqsTab || !faqs.length) return; faqsTab.innerHTML = `
${faqs.map((faq, index) => `
`).join('')}
`; } function toggleFAQ(index) { const content = document.getElementById(`faq-content-${index}`); const icon = document.getElementById(`faq-icon-${index}`); if (content && icon) { content.classList.toggle('hidden'); icon.classList.toggle('rotate-180'); } } function renderVideos(videos) { const videosTab = document.getElementById('videosTab'); if (!videosTab) return; if (!videos.length) { return; // Keep default content } videosTab.innerHTML = `
${videos.map(video => `
${video.duration || '5:00'}

${video.title}

${video.excerpt || ''}

${formatNumber(video.view_count || 0)} views ${formatTimeAgo(video.created_at)}
`).join('')}
`; } function renderTemplates(templates) { const templatesTab = document.getElementById('templatesTab'); if (!templatesTab) return; if (!templates || !templates.length) { return; // Keep default content } templatesTab.innerHTML = `
${templates.map(template => `

${template.name}

${template.description || ''}

${template.content?.substring(0, 200) || ''}...
`).join('')}
`; } function renderAnalytics(analytics) { const analyticsTab = document.getElementById('analyticsTab'); if (!analyticsTab || !analytics) return; analyticsTab.innerHTML = `
${formatNumber(analytics.total_views || 0)}
Total Views
${analytics.helpful_rate || 0}%
Helpful Rate
${formatNumber(analytics.searches_today || 0)}
Searches Today
${formatNumber(analytics.articles_updated || 0)}
Updated This Week

Top Articles

${(analytics.top_articles || []).map(article => `
${article.title} ${formatNumber(article.views)} views
`).join('')}
`; } function updateStatsUI(stats) { // Update sidebar stats if available if (stats) { const totalEl = document.querySelector('.knowledge-stats .text-2xl.text-primary-400'); if (totalEl && stats.total_articles) { totalEl.textContent = stats.total_articles; } } } function updateArticleCount(count) { const totalArticlesEl = document.getElementById('totalArticles'); if (totalArticlesEl) { totalArticlesEl.textContent = `${count} articles`; } } function updatePaginationInfo(total, page, perPage) { const start = (page - 1) * perPage + 1; const end = Math.min(page * perPage, total); const infoEl = document.querySelector('.text-sm.text-gray-400'); if (infoEl) { infoEl.innerHTML = `Showing ${start}-${end} of ${total} articles`; } } // ===================================================================================== // HELPER FUNCTIONS // ===================================================================================== function getCategoryClass(category) { const map = { 'orders': 'category-orders', 'payment': 'category-payment', 'delivery': 'category-delivery', 'product': 'category-product', 'account': 'category-account', 'technical': 'category-technical' }; return map[category?.toLowerCase()] || 'category-orders'; } function calculatePopularity(views, helpful) { const score = (views || 0) + (helpful || 0) * 10; if (score > 1000) return { label: 'Very High', percent: 89, color: 'success' }; if (score > 500) return { label: 'High', percent: 76, color: 'warning' }; if (score > 200) return { label: 'Medium', percent: 58, color: 'primary' }; return { label: 'Low', percent: 34, color: 'gray' }; } function calculateRating(article) { if (!article.helpful_count && !article.not_helpful_count) return '4.5'; const total = (article.helpful_count || 0) + (article.not_helpful_count || 0); if (total === 0) return '4.5'; const rating = ((article.helpful_count || 0) / total) * 5; return rating.toFixed(1); } function formatNumber(num) { if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M'; if (num >= 1000) return (num / 1000).toFixed(1) + 'k'; return num.toString(); } function formatTimeAgo(dateString) { if (!dateString) return 'Unknown'; const date = new Date(dateString); const now = new Date(); const diff = now - date; const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); const weeks = Math.floor(diff / 604800000); if (minutes < 60) return `${minutes}m ago`; if (hours < 24) return `${hours}h ago`; if (days < 7) return `${days}d ago`; return `${weeks}w ago`; } function highlightText(text, query) { if (!query || !text) return text; const regex = new RegExp(`(${query})`, 'gi'); return text.replace(regex, '$1'); } function trackAnalytics(event, data) { console.log('Analytics:', event, { ...data, timestamp: new Date().toISOString() }); // In production, send to analytics endpoint } function playVideo(url) { if (url) { window.open(url, '_blank'); } } async function copyTemplate(templateId) { try { const response = await authenticatedFetch(`/support/templates/${templateId}`); if (response.ok) { const template = await response.json(); await navigator.clipboard.writeText(template.content); showNotification('Template copied to clipboard!', 'success'); } } catch (error) { showNotification('Failed to copy template', 'error'); } } // Initialize document.addEventListener('DOMContentLoaded', function() { initializeSearch(); initializeTabs(); initializeViewToggle(); loadRecentActivity(); // Load initial data from API loadArticles(); loadCategories(); loadKnowledgeStats(); }); // Search functionality function initializeSearch() { const searchInput = document.getElementById('knowledgeSearch'); const searchSuggestions = document.getElementById('searchSuggestions'); if (searchInput) { searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { handleSearch(e.target.value); }, 300); }); searchInput.addEventListener('focus', function() { searchSuggestions.classList.remove('hidden'); loadSearchSuggestions(); }); searchInput.addEventListener('blur', function() { setTimeout(() => { searchSuggestions.classList.add('hidden'); }, 200); }); } // Initialize search suggestion click handlers initializeSearchSuggestions(); // Voice search document.getElementById('voiceSearch')?.addEventListener('click', function() { if ('webkitSpeechRecognition' in window) { const recognition = new webkitSpeechRecognition(); recognition.onresult = function(event) { const transcript = event.results[0][0].transcript; document.getElementById('knowledgeSearch').value = transcript; handleSearch(transcript); showNotification(`Searching for: "${transcript}"`, 'success'); }; recognition.onerror = function() { showNotification('Voice recognition failed. Please try again.', 'error'); }; recognition.start(); showNotification('Listening... Speak your search query', 'info'); } else { showNotification('Voice search not supported in this browser', 'warning'); } }); // Advanced search document.getElementById('advancedSearch')?.addEventListener('click', function() { showAdvancedSearchModal(); }); } /** * Initialize search suggestion click handlers */ function initializeSearchSuggestions() { document.querySelectorAll('.search-suggestion').forEach(suggestion => { suggestion.addEventListener('click', function() { const text = this.querySelector('span')?.textContent || ''; handleSearchSuggestionClick(text); }); }); } /** * Handle search suggestion click */ async function handleSearchSuggestionClick(suggestionText) { const searchInput = document.getElementById('knowledgeSearch'); const searchSuggestions = document.getElementById('searchSuggestions'); switch(suggestionText) { case 'Common Issues Today': await loadCommonIssues(); break; case 'Recent Updates': await loadRecentlyUpdatedArticles(); break; case 'Most Popular Articles': await loadMostPopularArticles(); break; default: if (searchInput) { searchInput.value = suggestionText; handleSearch(suggestionText); } } if (searchSuggestions) { searchSuggestions.classList.add('hidden'); } } /** * Load common issues from API * GET /api/v1/support/kb/common-issues */ async function loadCommonIssues() { try { showNotification('Loading common issues...', 'info'); // Try dedicated endpoint first let response = await authenticatedFetch('/support/kb/common-issues?limit=10'); if (!response.ok) { // Fallback: get most viewed articles from today response = await authenticatedFetch('/support/kb/articles?sort=view_countℴ=desc&per_page=10'); } if (!response.ok) { throw new Error('Failed to load common issues'); } const data = await response.json(); articlesCache.invalidate(); renderArticles(data); updateArticleCount(data.total || (data.items || data).length); showNotification('Showing common issues', 'success'); trackAnalytics('quick_access_clicked', { type: 'common_issues' }); } catch (error) { console.error('Error loading common issues:', error); showNotification('Failed to load common issues', 'error'); } } /** * Load recently updated articles * GET /api/v1/support/kb/articles?sort=updated_atℴ=desc */ async function loadRecentlyUpdatedArticles() { try { showNotification('Loading recent updates...', 'info'); const response = await authenticatedFetch('/support/kb/articles?sort=updated_atℴ=desc&per_page=12'); if (!response.ok) { throw new Error('Failed to load recent updates'); } const data = await response.json(); articlesCache.invalidate(); renderArticles(data); updateArticleCount(data.total || (data.items || data).length); showNotification('Showing recently updated articles', 'success'); trackAnalytics('quick_access_clicked', { type: 'recent_updates' }); } catch (error) { console.error('Error loading recent updates:', error); showNotification('Failed to load recent updates', 'error'); } } /** * Load most popular articles * GET /api/v1/support/kb/articles?sort=view_countℴ=desc */ async function loadMostPopularArticles() { try { showNotification('Loading popular articles...', 'info'); const response = await authenticatedFetch('/support/kb/articles?sort=view_countℴ=desc&per_page=12'); if (!response.ok) { throw new Error('Failed to load popular articles'); } const data = await response.json(); articlesCache.invalidate(); renderArticles(data); updateArticleCount(data.total || (data.items || data).length); showNotification('Showing most popular articles', 'success'); trackAnalytics('quick_access_clicked', { type: 'most_popular' }); } catch (error) { console.error('Error loading popular articles:', error); showNotification('Failed to load popular articles', 'error'); } } /** * Load dynamic search suggestions from API */ async function loadSearchSuggestions() { try { const response = await authenticatedFetch('/support/kb/suggestions?limit=5'); if (response.ok) { const suggestions = await response.json(); updateSearchSuggestionsUI(suggestions); } } catch (error) { // Keep default suggestions on error console.log('Using default search suggestions'); } } /** * Update search suggestions UI with API data */ function updateSearchSuggestionsUI(suggestions) { const container = document.querySelector('#searchSuggestions .space-y-1'); if (!container || !suggestions || !suggestions.length) return; // Keep the quick access items, add trending searches const existingItems = container.innerHTML; if (suggestions.trending && suggestions.trending.length > 0) { const trendingHTML = `
Trending Searches
${suggestions.trending.map(term => `
${term}
`).join('')}
`; container.innerHTML = existingItems + trendingHTML; } } /** * Show advanced search modal */ function showAdvancedSearchModal() { let modal = document.getElementById('advancedSearchModal'); if (!modal) { modal = document.createElement('div'); modal.id = 'advancedSearchModal'; modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4'; modal.innerHTML = `

Advanced Search

`; document.body.appendChild(modal); // Setup form submit handler document.getElementById('advancedSearchForm').addEventListener('submit', handleAdvancedSearch); } else { modal.classList.remove('hidden'); } } /** * Handle advanced search form submission */ async function handleAdvancedSearch(e) { e.preventDefault(); const params = new URLSearchParams(); const keywords = document.getElementById('advSearchKeywords').value; const category = document.getElementById('advSearchCategory').value; const status = document.getElementById('advSearchStatus').value; const dateFrom = document.getElementById('advSearchDateFrom').value; const dateTo = document.getElementById('advSearchDateTo').value; const tags = document.getElementById('advSearchTags').value; const sort = document.getElementById('advSearchSort').value; if (keywords) params.set('q', keywords); if (category) params.set('category_id', category); if (status) params.set('status', status); if (dateFrom) params.set('date_from', dateFrom); if (dateTo) params.set('date_to', dateTo); if (tags) params.set('tags', tags); if (sort && sort !== 'relevance') { params.set('sort', sort); params.set('order', 'desc'); } params.set('per_page', perPage); params.set('page', 1); try { showNotification('Searching...', 'info'); const response = await authenticatedFetch(`/support/kb/articles?${params}`); if (!response.ok) { throw new Error('Search failed'); } const data = await response.json(); // Update main search input if (keywords) { document.getElementById('knowledgeSearch').value = keywords; } // Invalidate cache and render results articlesCache.invalidate(); currentPage = 1; renderArticles(data, keywords); updateArticleCount(data.total || (data.items || data).length); closeAdvancedSearchModal(); showNotification(`Found ${data.total || (data.items || data).length} articles`, 'success'); trackAnalytics('advanced_search', { keywords, category, status, tags, results_count: data.total || (data.items || data).length }); } catch (error) { console.error('Advanced search error:', error); showNotification('Search failed. Please try again.', 'error'); } } /** * Reset advanced search form */ function resetAdvancedSearch() { document.getElementById('advancedSearchForm')?.reset(); } /** * Close advanced search modal */ function closeAdvancedSearchModal() { const modal = document.getElementById('advancedSearchModal'); if (modal) { modal.classList.add('hidden'); } } function handleSearch(query) { if (query.length < 2) { loadArticles(); // Reset to all articles return; } // Call API search searchArticles(query); } function filterArticles(query = '') { // Local filter for quick UI feedback while API loads const articles = document.querySelectorAll('.article-card'); articles.forEach(article => { const title = article.querySelector('h3')?.textContent?.toLowerCase() || ''; const content = article.querySelector('p')?.textContent?.toLowerCase() || ''; const isMatch = !query || title.includes(query.toLowerCase()) || content.includes(query.toLowerCase()); article.style.display = isMatch ? 'block' : 'none'; }); } // Tab functionality function initializeTabs() { document.querySelectorAll('[onclick^="switchTab"]').forEach(tab => { tab.addEventListener('click', function() { const tabName = this.onclick.toString().match(/'([^']+)'/)[1]; switchTab(tabName); }); }); } function switchTab(tabName) { // Update active tab activeTab = tabName; // Update tab buttons document.querySelectorAll('[onclick^="switchTab"]').forEach(tab => { tab.classList.remove('tab-active'); tab.classList.add('tab-inactive'); }); if (event && event.target) { event.target.classList.remove('tab-inactive'); event.target.classList.add('tab-active'); } // Update tab content document.querySelectorAll('.tab-content').forEach(content => { content.classList.add('hidden'); }); document.getElementById(tabName + 'Tab')?.classList.remove('hidden'); // Load tab-specific data from API switch(tabName) { case 'articles': loadArticles(); break; case 'videos': loadVideos(); break; case 'faqs': loadFAQs(); break; case 'templates': loadTemplates(); break; case 'analytics': loadAnalytics(); break; } // Track analytics trackAnalytics('tab_switched', { tab: tabName }); } // View toggle functionality function initializeViewToggle() { document.getElementById('gridViewBtn')?.addEventListener('click', function() { setView('grid'); }); document.getElementById('listViewBtn')?.addEventListener('click', function() { setView('list'); }); } function setView(viewType) { const gridBtn = document.getElementById('gridViewBtn'); const listBtn = document.getElementById('listViewBtn'); const articlesGrid = document.getElementById('articlesGrid'); if (viewType === 'grid') { gridBtn.classList.add('bg-primary-600', 'text-white'); gridBtn.classList.remove('text-gray-400'); listBtn.classList.remove('bg-primary-600', 'text-white'); listBtn.classList.add('text-gray-400'); articlesGrid.className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6'; } else { listBtn.classList.add('bg-primary-600', 'text-white'); listBtn.classList.remove('text-gray-400'); gridBtn.classList.remove('bg-primary-600', 'text-white'); gridBtn.classList.add('text-gray-400'); articlesGrid.className = 'space-y-4'; } } // Category filtering - calls API async function filterByCategory(category) { activeCategory = category; currentPage = 1; // Reset to first page // Update category buttons document.querySelectorAll('.category-filter').forEach(button => { button.classList.remove('bg-primary-600'); button.classList.add('bg-gray-700', 'hover:bg-gray-600'); }); if (event && event.target) { const targetBtn = event.target.closest('.category-filter') || event.target; targetBtn.classList.remove('bg-gray-700', 'hover:bg-gray-600'); targetBtn.classList.add('bg-primary-600'); } // Invalidate cache when category changes articlesCache.invalidate(); // Load articles from API with category filter if (category === 'all') { await loadArticles(); } else { await loadArticles({ category_id: category }); } // Track analytics trackAnalytics('category_filter', { category }); } // Article actions - API integrated async function openArticle(articleSlug) { const modal = document.getElementById('articleModal'); if (!modal) return; // Store slug for rating modal.dataset.slug = articleSlug; // Show modal with loading state modal.classList.remove('hidden'); const contentEl = document.getElementById('articleContent'); if (contentEl) { contentEl.innerHTML = '
'; } // Fetch article from API const article = await getArticle(articleSlug); if (article) { currentArticleId = article.id; // Update modal title and category const titleEl = document.getElementById('modalArticleTitle'); const categoryEl = document.getElementById('modalArticleCategory'); if (titleEl) titleEl.textContent = article.title; if (categoryEl) { categoryEl.textContent = article.category_name || 'General'; categoryEl.className = `category-tag ${getCategoryClass(article.category_name)}`; } // Render article content if (contentEl) { contentEl.innerHTML = `

${article.title}

Last updated: ${formatTimeAgo(article.updated_at)} by ${article.author_name || 'Admin'}

${article.content_html || article.content || '

No content available.

'}
`; } // Update sidebar info updateArticleSidebar(article); // Load related articles loadRelatedArticles(article.related_articles || [], article.tags); } trackAnalytics('article_opened', { articleSlug }); } function updateArticleSidebar(article) { // Update views, rating, etc. const viewsEl = document.querySelector('.article-sidebar .views-count'); if (viewsEl) viewsEl.textContent = formatNumber(article.view_count || 0); } /** * Load related articles from API * GET /api/v1/support/kb/articles/{slug}/related */ async function loadRelatedArticles(relatedIds, tags) { const relatedContainer = document.querySelector('#articleModal .space-y-3:has(.bg-gray-700.rounded-lg)'); if (!relatedContainer) return; try { const modal = document.getElementById('articleModal'); const slug = modal?.dataset?.slug; if (!slug) return; // Try to fetch related articles from API const response = await authenticatedFetch(`/support/kb/articles/${slug}/related?limit=5`); if (response.ok) { const relatedArticles = await response.json(); renderRelatedArticles(relatedArticles.items || relatedArticles); return; } // Fallback: if no related endpoint, search by tags if (tags && tags.length > 0) { const tagQuery = tags.slice(0, 2).join(','); const fallbackResponse = await authenticatedFetch(`/support/kb/articles?tags=${tagQuery}&per_page=5`); if (fallbackResponse.ok) { const data = await fallbackResponse.json(); const articles = (data.items || data).filter(a => a.slug !== slug); renderRelatedArticles(articles.slice(0, 3)); } } } catch (error) { console.log('Related articles: using static content'); // Keep static related articles on error } } /** * Render related articles in modal sidebar */ function renderRelatedArticles(articles) { const container = document.querySelector('#articleModal h4:contains("Related Articles")')?.parentElement?.querySelector('.space-y-3'); if (!container || !articles || articles.length === 0) return; container.innerHTML = articles.map(article => `
${article.title}
${article.category_name || 'General'}
`).join(''); } /** * Open related article (close current, open new) */ async function openRelatedArticle(slug) { // Track navigation trackAnalytics('related_article_clicked', { from_slug: document.getElementById('articleModal')?.dataset?.slug, to_slug: slug }); // Open the new article await openArticle(slug); } function closeArticle() { const modal = document.getElementById('articleModal'); if (modal) { modal.classList.add('hidden'); modal.dataset.slug = ''; } currentArticleId = null; } /** * Edit article - fetch and open editor */ async function editArticle() { if (!currentArticleId) { showNotification('No article selected', 'warning'); return; } try { const modal = document.getElementById('articleModal'); const slug = modal?.dataset?.slug; if (!slug) { showNotification('Article not found', 'error'); return; } showNotification('Loading article for editing...', 'info'); // Fetch full article data const response = await authenticatedFetch(`/support/kb/articles/${slug}`); if (!response.ok) { throw new Error('Failed to load article'); } const article = await response.json(); // Close view modal and open editor closeArticle(); showArticleEditorModal(article); } catch (error) { console.error('Error loading article for edit:', error); showNotification('Failed to load article for editing', 'error'); } } async function shareArticle() { const modal = document.getElementById('articleModal'); const slug = modal?.dataset?.slug; const url = slug ? `${window.location.origin}/knowledge/${slug}` : window.location.href; if (navigator.share) { try { await navigator.share({ title: document.getElementById('modalArticleTitle')?.textContent || 'Knowledge Base Article', text: 'Check out this helpful article', url: url }); } catch (error) { // User cancelled or error if (error.name !== 'AbortError') { await copyArticleLink(slug); } } } else { await copyArticleLink(slug); } } // rateArticle is defined in the API functions section above /** * Create new article - show creation modal */ function createNewArticle() { showArticleEditorModal(); } /** * Show article editor modal for create/edit */ function showArticleEditorModal(article = null) { let modal = document.getElementById('articleEditorModal'); if (!modal) { modal = document.createElement('div'); modal.id = 'articleEditorModal'; modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4'; modal.innerHTML = `

Create New Article

`; document.body.appendChild(modal); // Setup form submit handler document.getElementById('articleEditorForm').addEventListener('submit', handleArticleSubmit); } else { modal.classList.remove('hidden'); } // Populate form if editing if (article) { document.getElementById('editorModalTitle').textContent = 'Edit Article'; document.getElementById('editArticleId').value = article.id; document.getElementById('articleTitle').value = article.title || ''; document.getElementById('articleCategory').value = article.category_slug || article.category_name?.toLowerCase() || ''; document.getElementById('articleStatus').value = article.status || 'draft'; document.getElementById('articleTags').value = (article.tags || []).join(', '); document.getElementById('articleExcerpt').value = article.excerpt || ''; document.getElementById('articleContent').value = article.content || ''; } else { document.getElementById('editorModalTitle').textContent = 'Create New Article'; document.getElementById('editArticleId').value = ''; document.getElementById('articleEditorForm').reset(); } // Load categories from API loadCategoriesForEditor(); } /** * Load categories for editor dropdown */ async function loadCategoriesForEditor() { try { const response = await authenticatedFetch('/support/kb/categories'); if (!response.ok) return; const categories = await response.json(); const select = document.getElementById('articleCategory'); if (select && categories.length) { const currentValue = select.value; select.innerHTML = ''; categories.forEach(cat => { select.innerHTML += ``; }); if (currentValue) select.value = currentValue; } } catch (error) { console.log('Using default categories'); } } /** * Handle article form submission */ async function handleArticleSubmit(e) { e.preventDefault(); const articleId = document.getElementById('editArticleId').value; const articleData = { title: document.getElementById('articleTitle').value, category_id: document.getElementById('articleCategory').value, status: document.getElementById('articleStatus').value, tags: document.getElementById('articleTags').value.split(',').map(t => t.trim()).filter(t => t), excerpt: document.getElementById('articleExcerpt').value, content: document.getElementById('articleContent').value }; try { let response; if (articleId) { // Update existing article response = await authenticatedFetch(`/support/kb/articles/${articleId}`, { method: 'PUT', body: JSON.stringify(articleData) }); } else { // Create new article response = await authenticatedFetch('/support/kb/articles', { method: 'POST', body: JSON.stringify(articleData) }); } if (!response.ok) { const error = await response.json(); throw new Error(error.detail || 'Failed to save article'); } const result = await response.json(); showNotification(articleId ? 'Article updated successfully!' : 'Article created successfully!', 'success'); // Invalidate cache and reload articlesCache.invalidate(); await loadArticles(); closeArticleEditorModal(); // Track analytics trackAnalytics(articleId ? 'article_updated' : 'article_created', { article_id: result.id, title: articleData.title }); } catch (error) { console.error('Error saving article:', error); showNotification(error.message || 'Failed to save article', 'error'); } } /** * Save article as draft */ async function saveArticleDraft() { document.getElementById('articleStatus').value = 'draft'; document.getElementById('articleEditorForm').dispatchEvent(new Event('submit', { cancelable: true })); } /** * Preview article */ function previewArticle() { const content = document.getElementById('articleContent').value; const title = document.getElementById('articleTitle').value; // Open preview in new window const previewWindow = window.open('', '_blank'); previewWindow.document.write(` Preview: ${title}

${title}

${marked ? marked.parse(content) : content.replace(/\n/g, '
')}
`); previewWindow.document.close(); } /** * Format text in editor */ function formatText(format) { const textarea = document.getElementById('articleContent'); if (!textarea) return; const start = textarea.selectionStart; const end = textarea.selectionEnd; const selectedText = textarea.value.substring(start, end); let replacement = ''; switch(format) { case 'bold': replacement = `**${selectedText || 'bold text'}**`; break; case 'italic': replacement = `*${selectedText || 'italic text'}*`; break; case 'underline': replacement = `${selectedText || 'underlined text'}`; break; case 'heading': replacement = `\n## ${selectedText || 'Heading'}\n`; break; case 'list': replacement = `\n- ${selectedText || 'List item'}\n- \n- `; break; case 'numbered': replacement = `\n1. ${selectedText || 'First item'}\n2. \n3. `; break; case 'link': replacement = `[${selectedText || 'link text'}](url)`; break; case 'code': replacement = selectedText.includes('\n') ? `\n\`\`\`\n${selectedText || 'code here'}\n\`\`\`\n` : `\`${selectedText || 'code'}\``; break; } textarea.value = textarea.value.substring(0, start) + replacement + textarea.value.substring(end); textarea.focus(); textarea.setSelectionRange(start, start + replacement.length); } /** * Close article editor modal */ function closeArticleEditorModal() { const modal = document.getElementById('articleEditorModal'); if (modal) { modal.classList.add('hidden'); } } async function loadMoreArticles() { currentPage++; showNotification('Loading more articles...', 'info'); try { const params = {}; if (activeCategory !== 'all') { params.category_id = activeCategory; } const queryParams = new URLSearchParams({ page: currentPage, per_page: perPage, ...params }); const response = await authenticatedFetch(`/support/kb/articles?${queryParams}`); if (!response.ok) { throw new Error('Failed to load more articles'); } const data = await response.json(); const articles = data.items || data; if (articles.length === 0) { showNotification('No more articles to load', 'info'); currentPage--; return; } // Append new articles to grid const articlesGrid = document.getElementById('articlesGrid'); if (articlesGrid) { articles.forEach(article => { const categoryClass = getCategoryClass(article.category_name); const popularity = calculatePopularity(article.view_count, article.helpful_count); const articleCard = document.createElement('div'); articleCard.className = 'article-card glass-card rounded-xl p-6 fade-in'; articleCard.setAttribute('onclick', `openArticle('${article.slug}')`); articleCard.dataset.articleId = article.id; articleCard.innerHTML = `
${article.category_name || 'General'}
${calculateRating(article)}

${article.title}

${(article.excerpt || '').substring(0, 150)}...

Popularity ${popularity.label}
${formatNumber(article.view_count || 0)} views ${formatTimeAgo(article.updated_at)}
`; articlesGrid.appendChild(articleCard); }); } // Update pagination info updatePaginationInfo(data.total, currentPage, perPage); // Hide load more button if no more pages if (currentPage * perPage >= data.total) { const loadMoreBtn = document.querySelector('[onclick="loadMoreArticles()"]'); if (loadMoreBtn) { loadMoreBtn.style.display = 'none'; } } } catch (error) { console.error('Error loading more articles:', error); showNotification('Failed to load more articles', 'error'); currentPage--; } } // Bulk actions - defined in API functions section above // Load recent activity from API async function loadRecentActivity() { try { // Try to load recent activity from API const response = await authenticatedFetch('/support/kb/activity?limit=5'); if (response.ok) { const activities = await response.json(); renderRecentActivity(activities); } } catch (error) { console.log('Recent activity: using default data'); // Keep default static activity if API not available } } function renderRecentActivity(activities) { if (!activities || !activities.length) return; const activityContainer = document.querySelector('.recent-activity')?.parentElement; if (!activityContainer) return; activityContainer.innerHTML = `

Recent Activity

${activities.map(activity => { const activityClass = activity.type === 'created' ? 'activity-created' : activity.type === 'updated' ? 'activity-updated' : 'activity-accessed'; const icon = activity.type === 'created' ? 'plus' : activity.type === 'updated' ? 'edit' : 'eye'; return `
${activity.type === 'created' ? 'New article created' : activity.type === 'updated' ? 'Article updated' : 'Most accessed today'}
"${activity.title}" - ${formatTimeAgo(activity.timestamp)}
`; }).join('')}
`; } // Notification system function showNotification(message, type = 'info') { const notification = document.createElement('div'); const iconMap = { 'success': 'check-circle', 'error': 'exclamation-triangle', 'warning': 'exclamation-circle', 'info': 'info-circle' }; const colorMap = { 'success': 'bg-success-600', 'error': 'bg-danger-600', 'warning': 'bg-warning-600', 'info': 'bg-gray-600' }; notification.className = `fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg fade-in ${colorMap[type] || colorMap.info}`; notification.style.transition = 'all 0.3s ease'; notification.innerHTML = `

${message}

`; document.body.appendChild(notification); setTimeout(() => { notification.style.transform = 'translateX(100%)'; notification.style.opacity = '0'; setTimeout(() => notification.remove(), 300); }, 4000); } // Alias for consistency with other pages function showToast(message, type = 'info') { showNotification(message, type); } // UUIDv7 Generator function generateUUIDv7() { const timestamp = Date.now(); const timestampHex = timestamp.toString(16).padStart(12, '0'); const randomHex = Math.random().toString(16).substring(2, 14); return `${timestampHex.substring(0, 8)}-${timestampHex.substring(8, 12)}-7${randomHex.substring(0, 3)}-${Math.floor(Math.random() * 4 + 8).toString(16)}${randomHex.substring(3, 6)}-${randomHex.substring(6, 12)}`; } // Keyboard shortcuts document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeArticle(); } // Ctrl/Cmd + K to focus search if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); document.getElementById('knowledgeSearch')?.focus(); } // Tab navigation with numbers if (e.altKey) { switch(e.key) { case '1': switchTab('articles'); break; case '2': switchTab('videos'); break; case '3': switchTab('faqs'); break; case '4': switchTab('templates'); break; case '5': switchTab('analytics'); break; } } }); // Close modal on outside click document.getElementById('articleModal')?.addEventListener('click', function(e) { if (e.target === this) { closeArticle(); } }); // Real-time updates - poll for new articles periodically setInterval(async () => { try { // Reload stats periodically await loadKnowledgeStats(); } catch (error) { // Silently fail on background updates } }, 60000); // Every minute // Sort handler async function handleSortChange(sortValue) { currentSort = sortValue; currentPage = 1; articlesCache.invalidate(); const sortMap = { 'relevance': {}, 'updated': { sort: 'updated_at', order: 'desc' }, 'popular': { sort: 'view_count', order: 'desc' }, 'helpful': { sort: 'helpful_count', order: 'desc' }, 'created': { sort: 'created_at', order: 'desc' } }; await loadArticles(sortMap[sortValue] || {}); } // Initialize sort dropdown document.addEventListener('DOMContentLoaded', function() { const sortSelect = document.querySelector('select[aria-label="Select option"]'); if (sortSelect) { sortSelect.addEventListener('change', (e) => handleSortChange(e.target.value)); } }); // Quick action buttons in article modal document.addEventListener('DOMContentLoaded', function() { // Bookmark button const bookmarkBtn = document.querySelector('button:has(.fa-bookmark)'); if (bookmarkBtn) { bookmarkBtn.addEventListener('click', () => { if (currentArticleId) { bookmarkArticle(currentArticleId); } }); } // Print button const printBtn = document.querySelector('button:has(.fa-print)'); if (printBtn) { printBtn.addEventListener('click', printArticle); } // Copy link button const copyLinkBtn = document.querySelector('button:has(.fa-copy)'); if (copyLinkBtn) { copyLinkBtn.addEventListener('click', () => { const modal = document.getElementById('articleModal'); const slug = modal?.dataset?.slug; if (slug) { copyArticleLink(slug); } }); } }); // Track page view trackAnalytics('knowledge_management_view', { agentId: generateUUIDv7() });