513 lines
20 KiB
JavaScript
513 lines
20 KiB
JavaScript
|
|
// DOM Elements
|
|||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|||
|
|
// Navigation
|
|||
|
|
const menuItems = document.querySelectorAll('.menu li');
|
|||
|
|
const pages = document.querySelectorAll('.page');
|
|||
|
|
|
|||
|
|
// Generate Content Page
|
|||
|
|
const generateBtn = document.getElementById('generate-btn');
|
|||
|
|
const promptInput = document.getElementById('prompt');
|
|||
|
|
const contentTypeSelect = document.getElementById('content-type');
|
|||
|
|
const toneSelect = document.getElementById('tone');
|
|||
|
|
const lengthSelect = document.getElementById('length');
|
|||
|
|
const includeCTACheckbox = document.getElementById('include-cta');
|
|||
|
|
const referenceSimilarCheckbox = document.getElementById('reference-similar');
|
|||
|
|
const resultContainer = document.getElementById('result-container');
|
|||
|
|
const resultContent = document.getElementById('result-content');
|
|||
|
|
const loadingIndicator = document.getElementById('loading-indicator');
|
|||
|
|
const alignmentScore = document.getElementById('alignment-score');
|
|||
|
|
const suggestionsList = document.getElementById('suggestions-list');
|
|||
|
|
const copyBtn = document.getElementById('copy-btn');
|
|||
|
|
const improveBtn = document.getElementById('improve-btn');
|
|||
|
|
const saveBtn = document.getElementById('save-btn');
|
|||
|
|
const improvementPanel = document.getElementById('improvement-panel');
|
|||
|
|
const improvementFeedback = document.getElementById('improvement-feedback');
|
|||
|
|
const submitImprovement = document.getElementById('submit-improvement');
|
|||
|
|
|
|||
|
|
// Brand Style Page
|
|||
|
|
const toneSelector = document.getElementById('tone-selector');
|
|||
|
|
const voiceSelector = document.getElementById('voice-selector');
|
|||
|
|
const tabooWords = document.getElementById('taboo-words');
|
|||
|
|
const tabooInput = document.getElementById('taboo-input');
|
|||
|
|
const addTabooBtn = document.getElementById('add-taboo-btn');
|
|||
|
|
const avoidTerm = document.getElementById('avoid-term');
|
|||
|
|
const useTerm = document.getElementById('use-term');
|
|||
|
|
const addTermBtn = document.getElementById('add-term-btn');
|
|||
|
|
const saveBrandStyleBtn = document.getElementById('save-brand-style');
|
|||
|
|
const resetBrandStyleBtn = document.getElementById('reset-brand-style');
|
|||
|
|
|
|||
|
|
// Training Page
|
|||
|
|
const trainingTabs = document.querySelectorAll('.training-tabs .tab');
|
|||
|
|
const tabContents = document.querySelectorAll('.tab-content');
|
|||
|
|
const addTrainingBtn = document.getElementById('add-training-btn');
|
|||
|
|
const trainingContentType = document.getElementById('training-content-type');
|
|||
|
|
const campaignName = document.getElementById('campaign-name');
|
|||
|
|
const trainingContent = document.getElementById('training-content');
|
|||
|
|
const openRate = document.getElementById('open-rate');
|
|||
|
|
const clickRate = document.getElementById('click-rate');
|
|||
|
|
const conversionRate = document.getElementById('conversion-rate');
|
|||
|
|
|
|||
|
|
// API Base URL
|
|||
|
|
const API_URL = 'http://localhost:8000';
|
|||
|
|
|
|||
|
|
// Menu Navigation
|
|||
|
|
menuItems.forEach(item => {
|
|||
|
|
item.addEventListener('click', function() {
|
|||
|
|
const pageName = this.getAttribute('data-page');
|
|||
|
|
|
|||
|
|
// Update active menu item
|
|||
|
|
menuItems.forEach(menuItem => menuItem.classList.remove('active'));
|
|||
|
|
this.classList.add('active');
|
|||
|
|
|
|||
|
|
// Show selected page
|
|||
|
|
pages.forEach(page => {
|
|||
|
|
if (page.id === `${pageName}-page`) {
|
|||
|
|
page.classList.add('active');
|
|||
|
|
} else {
|
|||
|
|
page.classList.remove('active');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Generate Content
|
|||
|
|
if (generateBtn) {
|
|||
|
|
generateBtn.addEventListener('click', function() {
|
|||
|
|
if (!promptInput.value.trim()) {
|
|||
|
|
alert('Please enter a prompt for content generation.');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Show loading indicator
|
|||
|
|
loadingIndicator.classList.remove('hidden');
|
|||
|
|
resultContainer.classList.add('hidden');
|
|||
|
|
|
|||
|
|
// Prepare request data
|
|||
|
|
const requestData = {
|
|||
|
|
prompt: promptInput.value,
|
|||
|
|
content_type: contentTypeSelect.value || null,
|
|||
|
|
tone: toneSelect.value || null,
|
|||
|
|
length: lengthSelect.value || null,
|
|||
|
|
include_cta: includeCTACheckbox.checked,
|
|||
|
|
reference_similar_content: referenceSimilarCheckbox.checked
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Call the API
|
|||
|
|
fetch(`${API_URL}/generate-copy`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(requestData)
|
|||
|
|
})
|
|||
|
|
.then(response => {
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
return response.json();
|
|||
|
|
})
|
|||
|
|
.then(data => {
|
|||
|
|
// Hide loading indicator
|
|||
|
|
loadingIndicator.classList.add('hidden');
|
|||
|
|
|
|||
|
|
// Display result
|
|||
|
|
resultContent.textContent = data.content;
|
|||
|
|
|
|||
|
|
// Set alignment score
|
|||
|
|
const score = data.metadata.alignment_score || 0;
|
|||
|
|
alignmentScore.style.width = `${score}%`;
|
|||
|
|
alignmentScore.textContent = `${Math.round(score)}%`;
|
|||
|
|
|
|||
|
|
if (score < 60) {
|
|||
|
|
alignmentScore.style.backgroundColor = 'var(--danger-color)';
|
|||
|
|
} else if (score < 80) {
|
|||
|
|
alignmentScore.style.backgroundColor = 'var(--warning-color)';
|
|||
|
|
} else {
|
|||
|
|
alignmentScore.style.backgroundColor = 'var(--success-color)';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Display suggestions
|
|||
|
|
if (data.suggestions && data.suggestions.length > 0) {
|
|||
|
|
suggestionsList.innerHTML = '';
|
|||
|
|
data.suggestions.forEach(suggestion => {
|
|||
|
|
const li = document.createElement('li');
|
|||
|
|
li.textContent = suggestion;
|
|||
|
|
li.addEventListener('click', function() {
|
|||
|
|
promptInput.value = suggestion;
|
|||
|
|
});
|
|||
|
|
suggestionsList.appendChild(li);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Show result container
|
|||
|
|
resultContainer.classList.remove('hidden');
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
console.error('Error:', error);
|
|||
|
|
loadingIndicator.classList.add('hidden');
|
|||
|
|
alert('An error occurred while generating content. Please try again.');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Copy to Clipboard
|
|||
|
|
if (copyBtn) {
|
|||
|
|
copyBtn.addEventListener('click', function() {
|
|||
|
|
navigator.clipboard.writeText(resultContent.textContent)
|
|||
|
|
.then(() => {
|
|||
|
|
const originalTitle = copyBtn.getAttribute('title');
|
|||
|
|
copyBtn.setAttribute('title', 'Copied!');
|
|||
|
|
setTimeout(() => {
|
|||
|
|
copyBtn.setAttribute('title', originalTitle);
|
|||
|
|
}, 2000);
|
|||
|
|
})
|
|||
|
|
.catch(err => {
|
|||
|
|
console.error('Could not copy text: ', err);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Toggle Improvement Panel
|
|||
|
|
if (improveBtn) {
|
|||
|
|
improveBtn.addEventListener('click', function() {
|
|||
|
|
improvementPanel.classList.toggle('hidden');
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Submit Improvement Feedback
|
|||
|
|
if (submitImprovement) {
|
|||
|
|
submitImprovement.addEventListener('click', function() {
|
|||
|
|
if (!improvementFeedback.value.trim()) {
|
|||
|
|
alert('Please enter feedback for improvement.');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Show loading indicator
|
|||
|
|
loadingIndicator.classList.remove('hidden');
|
|||
|
|
|
|||
|
|
// Prepare request data
|
|||
|
|
const requestData = {
|
|||
|
|
content: resultContent.textContent,
|
|||
|
|
feedback: improvementFeedback.value
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Call the API
|
|||
|
|
fetch(`${API_URL}/improve-content`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(requestData)
|
|||
|
|
})
|
|||
|
|
.then(response => {
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
return response.json();
|
|||
|
|
})
|
|||
|
|
.then(data => {
|
|||
|
|
// Hide loading indicator
|
|||
|
|
loadingIndicator.classList.add('hidden');
|
|||
|
|
|
|||
|
|
// Update result content
|
|||
|
|
resultContent.textContent = data.improved_content;
|
|||
|
|
|
|||
|
|
// Hide improvement panel
|
|||
|
|
improvementPanel.classList.add('hidden');
|
|||
|
|
improvementFeedback.value = '';
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
console.error('Error:', error);
|
|||
|
|
loadingIndicator.classList.add('hidden');
|
|||
|
|
alert('An error occurred while improving content. Please try again.');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Save Content to History
|
|||
|
|
if (saveBtn) {
|
|||
|
|
saveBtn.addEventListener('click', function() {
|
|||
|
|
alert('Content saved to history!');
|
|||
|
|
// In a real implementation, you would save this to local storage
|
|||
|
|
// or call an API to save it to the backend
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Brand Style Tag Selection
|
|||
|
|
if (toneSelector) {
|
|||
|
|
const tagElements = toneSelector.querySelectorAll('.tag');
|
|||
|
|
tagElements.forEach(tag => {
|
|||
|
|
tag.addEventListener('click', function() {
|
|||
|
|
this.classList.toggle('selected');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (voiceSelector) {
|
|||
|
|
const tagElements = voiceSelector.querySelectorAll('.tag');
|
|||
|
|
tagElements.forEach(tag => {
|
|||
|
|
tag.addEventListener('click', function() {
|
|||
|
|
this.classList.toggle('selected');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add Taboo Word
|
|||
|
|
if (addTabooBtn && tabooInput && tabooWords) {
|
|||
|
|
addTabooBtn.addEventListener('click', function() {
|
|||
|
|
const word = tabooInput.value.trim();
|
|||
|
|
if (word) {
|
|||
|
|
const tagElement = document.createElement('span');
|
|||
|
|
tagElement.classList.add('tag', 'removable');
|
|||
|
|
tagElement.innerHTML = `${word}<i class="fas fa-times"></i>`;
|
|||
|
|
|
|||
|
|
// Add click event to remove the tag
|
|||
|
|
tagElement.querySelector('i').addEventListener('click', function() {
|
|||
|
|
tagElement.remove();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
tabooWords.appendChild(tagElement);
|
|||
|
|
tabooInput.value = '';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Allow pressing Enter to add a word
|
|||
|
|
tabooInput.addEventListener('keypress', function(e) {
|
|||
|
|
if (e.key === 'Enter') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
addTabooBtn.click();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add Terminology Term
|
|||
|
|
if (addTermBtn && avoidTerm && useTerm) {
|
|||
|
|
addTermBtn.addEventListener('click', function() {
|
|||
|
|
const avoid = avoidTerm.value.trim();
|
|||
|
|
const use = useTerm.value.trim();
|
|||
|
|
|
|||
|
|
if (avoid && use) {
|
|||
|
|
const tableRow = document.createElement('div');
|
|||
|
|
tableRow.classList.add('terminology-row');
|
|||
|
|
tableRow.innerHTML = `
|
|||
|
|
<div class="term-avoid">${avoid}</div>
|
|||
|
|
<div class="term-use">${use}</div>
|
|||
|
|
<div class="term-actions">
|
|||
|
|
<button class="btn btn-icon"><i class="fas fa-times"></i></button>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
// Add click event to remove the row
|
|||
|
|
tableRow.querySelector('.btn-icon').addEventListener('click', function() {
|
|||
|
|
tableRow.remove();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Insert before the add row
|
|||
|
|
const addRow = document.querySelector('.terminology-row.add-row');
|
|||
|
|
addRow.parentNode.insertBefore(tableRow, addRow);
|
|||
|
|
|
|||
|
|
avoidTerm.value = '';
|
|||
|
|
useTerm.value = '';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Allow pressing Enter to add a term
|
|||
|
|
useTerm.addEventListener('keypress', function(e) {
|
|||
|
|
if (e.key === 'Enter') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
addTermBtn.click();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Save Brand Style
|
|||
|
|
if (saveBrandStyleBtn) {
|
|||
|
|
saveBrandStyleBtn.addEventListener('click', function() {
|
|||
|
|
// Collect tone tags
|
|||
|
|
const selectedTones = [];
|
|||
|
|
toneSelector.querySelectorAll('.tag.selected').forEach(tag => {
|
|||
|
|
selectedTones.push(tag.textContent);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Collect voice characteristics
|
|||
|
|
const selectedVoice = [];
|
|||
|
|
voiceSelector.querySelectorAll('.tag.selected').forEach(tag => {
|
|||
|
|
selectedVoice.push(tag.textContent);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Collect taboo words
|
|||
|
|
const tabooWordsList = [];
|
|||
|
|
tabooWords.querySelectorAll('.tag').forEach(tag => {
|
|||
|
|
// Extract just the text without the 'x' icon
|
|||
|
|
const text = tag.textContent.replace('×', '').trim();
|
|||
|
|
tabooWordsList.push(text);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Collect preferred terms
|
|||
|
|
const preferredTerms = {};
|
|||
|
|
document.querySelectorAll('.terminology-row:not(.add-row):not(.terminology-header)').forEach(row => {
|
|||
|
|
const avoid = row.querySelector('.term-avoid').textContent.trim();
|
|||
|
|
const use = row.querySelector('.term-use').textContent.trim();
|
|||
|
|
if (avoid && use) {
|
|||
|
|
preferredTerms[avoid] = use;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Prepare request data
|
|||
|
|
const requestData = {
|
|||
|
|
tone: selectedTones,
|
|||
|
|
voice_characteristics: selectedVoice,
|
|||
|
|
taboo_words: tabooWordsList,
|
|||
|
|
preferred_terms: preferredTerms
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Call the API
|
|||
|
|
fetch(`${API_URL}/brand-style`, {
|
|||
|
|
method: 'PUT',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(requestData)
|
|||
|
|
})
|
|||
|
|
.then(response => {
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
return response.json();
|
|||
|
|
})
|
|||
|
|
.then(data => {
|
|||
|
|
alert('Brand style updated successfully!');
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
console.error('Error:', error);
|
|||
|
|
alert('An error occurred while updating brand style. Please try again.');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Reset Brand Style
|
|||
|
|
if (resetBrandStyleBtn) {
|
|||
|
|
resetBrandStyleBtn.addEventListener('click', function() {
|
|||
|
|
if (confirm('Are you sure you want to reset brand style to defaults?')) {
|
|||
|
|
// In a real implementation, you would call an API to reset
|
|||
|
|
// For now, just reload the page
|
|||
|
|
window.location.reload();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Training Tabs
|
|||
|
|
if (trainingTabs.length > 0) {
|
|||
|
|
trainingTabs.forEach(tab => {
|
|||
|
|
tab.addEventListener('click', function() {
|
|||
|
|
const tabName = this.getAttribute('data-tab');
|
|||
|
|
|
|||
|
|
// Update active tab
|
|||
|
|
trainingTabs.forEach(t => t.classList.remove('active'));
|
|||
|
|
this.classList.add('active');
|
|||
|
|
|
|||
|
|
// Show selected tab content
|
|||
|
|
tabContents.forEach(content => {
|
|||
|
|
if (content.id === `${tabName}-tab`) {
|
|||
|
|
content.classList.add('active');
|
|||
|
|
} else {
|
|||
|
|
content.classList.remove('active');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add Training Data
|
|||
|
|
if (addTrainingBtn) {
|
|||
|
|
addTrainingBtn.addEventListener('click', function() {
|
|||
|
|
if (!trainingContentType.value) {
|
|||
|
|
alert('Please select a content type.');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!trainingContent.value.trim()) {
|
|||
|
|
alert('Please enter content for training.');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Prepare request data
|
|||
|
|
const requestData = {
|
|||
|
|
content_type: trainingContentType.value,
|
|||
|
|
content: trainingContent.value,
|
|||
|
|
metadata: {
|
|||
|
|
campaign_name: campaignName.value,
|
|||
|
|
performance_metrics: {}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Add performance metrics if provided
|
|||
|
|
if (openRate.value) {
|
|||
|
|
requestData.metadata.performance_metrics.open_rate = parseFloat(openRate.value) / 100;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (clickRate.value) {
|
|||
|
|
requestData.metadata.performance_metrics.click_rate = parseFloat(clickRate.value) / 100;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (conversionRate.value) {
|
|||
|
|
requestData.metadata.performance_metrics.conversion_rate = parseFloat(conversionRate.value) / 100;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Call the API
|
|||
|
|
fetch(`${API_URL}/training-data`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(requestData)
|
|||
|
|
})
|
|||
|
|
.then(response => {
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
return response.json();
|
|||
|
|
})
|
|||
|
|
.then(data => {
|
|||
|
|
alert('Training data added successfully!');
|
|||
|
|
|
|||
|
|
// Clear form
|
|||
|
|
trainingContentType.value = '';
|
|||
|
|
campaignName.value = '';
|
|||
|
|
trainingContent.value = '';
|
|||
|
|
openRate.value = '';
|
|||
|
|
clickRate.value = '';
|
|||
|
|
conversionRate.value = '';
|
|||
|
|
|
|||
|
|
// Switch to view tab
|
|||
|
|
document.querySelector('.tab[data-tab="view-training"]').click();
|
|||
|
|
|
|||
|
|
// In a real implementation, you would also refresh the training data list
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
console.error('Error:', error);
|
|||
|
|
alert('An error occurred while adding training data. Please try again.');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Load Brand Style on Page Load
|
|||
|
|
fetch(`${API_URL}/brand-style`)
|
|||
|
|
.then(response => {
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
return response.json();
|
|||
|
|
})
|
|||
|
|
.then(data => {
|
|||
|
|
console.log('Loaded brand style:', data);
|
|||
|
|
// In a real implementation, you would update the UI based on the loaded data
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
console.error('Error loading brand style:', error);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// For demonstration purposes, let's create a mocked pre-filled content
|
|||
|
|
// In a real implementation, this would be loaded from the backend
|
|||
|
|
document.getElementById('prompt').value = 'Write a social media post about our new coaching program';
|
|||
|
|
});
|