// Idea House JavaScript - Handles data field selection and Coze API processing // Global variables let dataFields = []; let filteredDataFields = []; let selectedFields = new Map(); // Map of field_id -> description // Filter state variables let columnFilters = { id: '', description: '', type: '', coverage: { min: null, max: null }, userCount: null, alphaCount: null }; let sortColumn = null; let sortOrder = 'asc'; // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', function() { // Set up event listeners document.getElementById('loadDataFieldsBtn').addEventListener('click', loadDataFields); document.getElementById('clearSelectionBtn').addEventListener('click', clearSelection); document.getElementById('processFieldsBtn').addEventListener('click', processSelectedFields); // Set up API token toggle button const toggleBtn = document.getElementById('toggleApiTokenBtn'); const tokenInput = document.getElementById('cozeApiTokenInput'); toggleBtn.addEventListener('click', function() { if (tokenInput.type === 'password') { tokenInput.type = 'text'; toggleBtn.textContent = 'Hide'; } else { tokenInput.type = 'password'; toggleBtn.textContent = 'Show'; } }); // Load saved configuration loadSavedCozeConfig(); // Set up save configuration button document.getElementById('saveCozeConfigBtn').addEventListener('click', saveCozeConfig); // Set up clear configuration button document.getElementById('clearCozeConfigBtn').addEventListener('click', clearCozeConfig); // Set up filter event listeners setupFilterEventListeners(); }); // Load data fields from BRAIN API async function loadDataFields() { const region = document.getElementById('regionInput').value; const delay = document.getElementById('delayInput').value; const universe = document.getElementById('universeInput').value; const datasetId = document.getElementById('datasetInput').value; // Show loading state document.getElementById('dataFieldsStats').textContent = 'Loading data fields...'; document.getElementById('tableContainer').style.display = 'none'; try { // Get session ID from localStorage or cookie const sessionId = localStorage.getItem('brain_session_id'); if (!sessionId) { alert('Please connect to BRAIN first from the main page'); return; } // Call the proxy endpoint const params = new URLSearchParams({ region: region, delay: delay, universe: universe, dataset_id: datasetId }); // Log the API calls console.log('🚀 Making API calls to fetch data fields and dataset description'); console.log('📋 Parameters:', { region: region, delay: delay, universe: universe, dataset_id: datasetId }); // Fetch data fields and dataset description in parallel const [dataFieldsResponse, descriptionResponse] = await Promise.all([ fetch(`/idea-house/api/get-datafields-proxy?${params}`, { method: 'GET', headers: { 'Session-ID': sessionId } }), fetch(`/idea-house/api/get-dataset-description?${params}`, { method: 'GET', headers: { 'Session-ID': sessionId } }) ]); console.log('📥 Received responses:'); console.log(' Data fields status:', dataFieldsResponse.status); console.log(' Dataset description status:', descriptionResponse.status); if (!dataFieldsResponse.ok) { const errorData = await dataFieldsResponse.json(); throw new Error(errorData.error || 'Failed to fetch data fields'); } dataFields = await dataFieldsResponse.json(); // Get dataset description if available let datasetDescription = ''; if (descriptionResponse.ok) { console.log('✅ Dataset description response OK, parsing JSON...'); const descriptionData = await descriptionResponse.json(); console.log('📄 Description data:', descriptionData); if (descriptionData.success) { datasetDescription = descriptionData.description; // Store it globally for later use window.currentDatasetDescription = datasetDescription; console.log('✅ Dataset description stored:', datasetDescription); } else { console.log('⚠️ Description response success=false'); } } else { console.log('❌ Dataset description response not OK:', descriptionResponse.status); try { const errorData = await descriptionResponse.json(); console.log('❌ Error details:', errorData); } catch (e) { console.log('❌ Could not parse error response'); } } // Update stats document.getElementById('dataFieldsStats').textContent = `Loaded ${dataFields.length} data fields`; // Populate table with dataset description populateDataFieldsTable(datasetDescription); // Show table document.getElementById('tableContainer').style.display = 'block'; } catch (error) { console.error('Failed to load data fields:', error); document.getElementById('dataFieldsStats').textContent = `Error: ${error.message}`; alert(`Failed to load data fields: ${error.message}`); } } // Populate the data fields table with filtering and sorting function populateDataFieldsTable(datasetDescription) { console.log('📊 populateDataFieldsTable called with description:', datasetDescription); const tableBody = document.getElementById('dataFieldsTableBody'); const highCoverageFilter = document.getElementById('filterHighCoverage')?.checked || false; const popularFilter = document.getElementById('filterPopular')?.checked || false; const matrixOnlyFilter = document.getElementById('filterMatrixOnly')?.checked || false; // Display dataset description if available if (datasetDescription) { console.log('🎯 Displaying passed dataset description'); displayDatasetDescription(datasetDescription); } else if (window.currentDatasetDescription) { console.log('🎯 Displaying stored dataset description'); displayDatasetDescription(window.currentDatasetDescription); } else { console.log('⚠️ No dataset description available'); } // Apply filters filteredDataFields = dataFields.filter(field => { // Column-specific filters // ID filter if (columnFilters.id && !field.id.toLowerCase().includes(columnFilters.id.toLowerCase())) { return false; } // Description filter if (columnFilters.description && !field.description.toLowerCase().includes(columnFilters.description.toLowerCase())) { return false; } // Type filter if (columnFilters.type && field.type !== columnFilters.type) { return false; } // Coverage range filter if (columnFilters.coverage.min !== null && field.coverage * 100 < columnFilters.coverage.min) { return false; } if (columnFilters.coverage.max !== null && field.coverage * 100 > columnFilters.coverage.max) { return false; } // User count filter if (columnFilters.userCount !== null && field.userCount < columnFilters.userCount) { return false; } // Alpha count filter if (columnFilters.alphaCount !== null && field.alphaCount < columnFilters.alphaCount) { return false; } // High coverage filter if (highCoverageFilter && field.coverage < 0.9) { return false; } // Popular filter if (popularFilter && field.userCount < 1000) { return false; } // Matrix type filter if (matrixOnlyFilter && field.type !== 'MATRIX') { return false; } return true; }); // Sort filtered data fields if (sortColumn) { filteredDataFields.sort((a, b) => { let aVal = a[sortColumn]; let bVal = b[sortColumn]; // Handle numeric values if (sortColumn === 'coverage' || sortColumn === 'userCount' || sortColumn === 'alphaCount') { aVal = Number(aVal); bVal = Number(bVal); } else { // String comparison aVal = String(aVal).toLowerCase(); bVal = String(bVal).toLowerCase(); } if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1; if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1; return 0; }); } // Clear table tableBody.innerHTML = ''; if (filteredDataFields.length === 0) { tableBody.innerHTML = '
Processing ${selectedFields.size} selected fields through Coze workflow
📡 Connecting to Coze API...
⚙️ Running workflow analysis...
📊 Generating insights...
Workflow ID: ${workflowId}
Selected Fields: ${Object.keys(selectedFieldsObject).join(', ')}
✅ API credentials validated
🔄 Sending request to Coze servers...
⏳ Please wait for response...
Successfully processed through workflow
📥 Response received from Coze
🎉 Formatting results...
Please wait while we analyze your selected fields...
`; } } // Display results in markdown format function displayResults(result) { const resultsSection = document.getElementById('resultsSection'); const resultsContent = document.getElementById('resultsContent'); // Show results section resultsSection.style.display = 'block'; // Format the results as markdown - simplified version let markdown = '# Analysis Results\n\n'; // Add selected fields section markdown += '## Selected Fields\n\n'; Object.entries(result.selected_fields).forEach(([fieldId, description]) => { markdown += `- **${fieldId}**: ${description}\n`; }); markdown += '\n'; // Add output section - only show the actual analysis output markdown += '## Analysis Output\n\n'; if (result.output) { // If the output is already formatted text, use it directly if (typeof result.output === 'string') { markdown += result.output; } else { // If it's an object, try to display it nicely markdown += '```json\n'; markdown += JSON.stringify(result.output, null, 2); markdown += '\n```\n'; } } else { markdown += '_No output data available_'; } // Render the markdown as HTML resultsContent.innerHTML = renderMarkdown(markdown); // Scroll to results resultsSection.scrollIntoView({ behavior: 'smooth' }); } // Helper function to format markdown (optional enhancement) function renderMarkdown(markdown) { // This is an improved markdown renderer that handles lists better let html = markdown; // First, escape HTML to prevent XSS html = html.replace(/&/g, '&') .replace(//g, '>'); // Code blocks (must be before inline code) html = html.replace(/```([\s\S]*?)```/g, function(match, code) { return '' + code.trim() + '';
});
// Headers
html = html.replace(/^#### (.*$)/gim, '$1');
// Handle lists more carefully
// Split into lines for better list processing
const lines = html.split('\n');
let inList = false;
let processedLines = [];
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
// Check if line is a list item
if (line.match(/^[\*\-\+] /)) {
// Replace list marker with proper HTML
line = line.replace(/^[\*\-\+] (.*)$/, ''); html = '
' + html + '
'; // Clean up empty paragraphs html = html.replace(/\s*<\/p>/g, ''); html = html.replace(/
( (|
|
)/g, '$1');
html = html.replace(/(<\/ul>|<\/ol>|<\/pre>)<\/p>/g, '$1');
// Single line breaks within paragraphs
html = html.replace(/\n/g, '
');
return html;
}
// Save Coze configuration to localStorage
function saveCozeConfig() {
const cozeApiToken = document.getElementById('cozeApiTokenInput').value;
const workflowId = document.getElementById('workflowIdInput').value;
// Save to localStorage
localStorage.setItem('coze_api_token', cozeApiToken);
localStorage.setItem('coze_workflow_id', workflowId);
// Show success message
const messageElement = document.getElementById('saveConfigMessage');
messageElement.style.display = 'inline';
// Hide message after 3 seconds
setTimeout(() => {
messageElement.style.display = 'none';
}, 3000);
}
// Load saved Coze configuration from localStorage
function loadSavedCozeConfig() {
const savedToken = localStorage.getItem('coze_api_token');
const savedWorkflowId = localStorage.getItem('coze_workflow_id');
if (savedToken) {
document.getElementById('cozeApiTokenInput').value = savedToken;
}
if (savedWorkflowId) {
document.getElementById('workflowIdInput').value = savedWorkflowId;
}
}
// Clear Coze configuration and reset to defaults
function clearCozeConfig() {
// Remove from localStorage
localStorage.removeItem('coze_api_token');
localStorage.removeItem('coze_workflow_id');
// Reset to default values
document.getElementById('cozeApiTokenInput').value = 'pat_OCxUpnmL7hCvUxEWwcKL5XwUOdoiA3eWLzwY6L8W9sQVN1saJnoMrDNyhFhEn63l';
document.getElementById('workflowIdInput').value = '7522912027267956786';
// Show message
const messageElement = document.getElementById('saveConfigMessage');
messageElement.textContent = 'Configuration reset to default!';
messageElement.style.color = '#ff9800';
messageElement.style.display = 'inline';
// Hide message after 3 seconds
setTimeout(() => {
messageElement.style.display = 'none';
messageElement.textContent = 'Configuration saved!';
messageElement.style.color = '#4caf50';
}, 3000);
}
// Set up filter event listeners
function setupFilterEventListeners() {
// Quick filter checkboxes
const highCoverageFilter = document.getElementById('filterHighCoverage');
const popularFilter = document.getElementById('filterPopular');
const matrixOnlyFilter = document.getElementById('filterMatrixOnly');
// Filter action buttons
const selectAllBtn = document.getElementById('selectAllFiltered');
const clearAllBtn = document.getElementById('clearAllSelected');
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
// Set up quick filter listeners
if (highCoverageFilter) highCoverageFilter.onchange = () => populateDataFieldsTable();
if (popularFilter) popularFilter.onchange = () => populateDataFieldsTable();
if (matrixOnlyFilter) matrixOnlyFilter.onchange = () => populateDataFieldsTable();
// Set up action button listeners
if (selectAllBtn) selectAllBtn.onclick = selectAllFilteredFields;
if (clearAllBtn) clearAllBtn.onclick = clearAllSelectedFields;
if (selectAllCheckbox) {
selectAllCheckbox.onclick = (e) => {
e.stopPropagation();
if (selectAllCheckbox.checked) {
selectAllFilteredFields();
} else {
clearAllFilteredFields();
}
};
}
// Column filter listeners
document.querySelectorAll('.column-filter').forEach(filter => {
filter.addEventListener('input', (e) => {
const column = e.target.dataset.column;
const value = e.target.value;
if (column === 'userCount' || column === 'alphaCount') {
columnFilters[column] = value ? parseInt(value) : null;
} else {
columnFilters[column] = value;
}
// Add/remove active class
if (value) {
e.target.classList.add('active');
} else {
e.target.classList.remove('active');
}
populateDataFieldsTable();
});
});
// Coverage range filters
document.querySelectorAll('.column-filter-min, .column-filter-max').forEach(filter => {
filter.addEventListener('input', (e) => {
const isMin = e.target.classList.contains('column-filter-min');
const value = e.target.value;
if (isMin) {
columnFilters.coverage.min = value ? parseFloat(value) : null;
} else {
columnFilters.coverage.max = value ? parseFloat(value) : null;
}
// Add/remove active class
const minInput = e.target.parentElement.querySelector('.column-filter-min');
const maxInput = e.target.parentElement.querySelector('.column-filter-max');
if (minInput.value || maxInput.value) {
minInput.classList.add('active');
maxInput.classList.add('active');
} else {
minInput.classList.remove('active');
maxInput.classList.remove('active');
}
populateDataFieldsTable();
});
});
// Sort button listeners
document.querySelectorAll('.sort-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const column = e.target.dataset.column;
// Reset all other sort buttons
document.querySelectorAll('.sort-btn').forEach(b => {
if (b !== e.target) {
b.classList.remove('asc', 'desc');
b.dataset.order = 'asc';
}
});
// Toggle sort order
if (sortColumn === column) {
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
} else {
sortColumn = column;
sortOrder = 'asc';
}
e.target.dataset.order = sortOrder;
e.target.classList.remove('asc', 'desc');
e.target.classList.add(sortOrder);
populateDataFieldsTable();
});
});
}
// Update data fields statistics
function updateDataFieldsStats() {
const dataFieldsCountEl = document.getElementById('dataFieldsCount');
const filteredCountEl = document.getElementById('filteredCount');
const selectedCountEl = document.getElementById('selectedFieldsCount');
if (dataFieldsCountEl) dataFieldsCountEl.textContent = `${dataFields.length} fields loaded`;
if (filteredCountEl) filteredCountEl.textContent = `${filteredDataFields.length} filtered`;
if (selectedCountEl) selectedCountEl.textContent = `${selectedFields.size} selected`;
}
// Populate type filter dropdown
function populateTypeFilter() {
const typeFilter = document.getElementById('typeFilter');
if (!typeFilter) return;
// Get unique types from current data fields
const uniqueTypes = [...new Set(dataFields.map(field => field.type))].sort();
// Clear existing options except "All Types"
typeFilter.innerHTML = '';
uniqueTypes.forEach(type => {
const option = document.createElement('option');
option.value = type;
option.textContent = type;
typeFilter.appendChild(option);
});
// Restore selected value if it exists
if (columnFilters.type && uniqueTypes.includes(columnFilters.type)) {
typeFilter.value = columnFilters.type;
}
}
// Select all filtered fields
function selectAllFilteredFields() {
filteredDataFields.forEach(field => {
selectedFields.set(field.id, field.description);
const row = document.querySelector(`tr[data-field-id="${field.id}"]`);
if (row) {
const checkbox = row.querySelector('.field-checkbox');
checkbox.checked = true;
row.classList.add('selected');
}
});
updateSelectedFieldsDisplay();
updateDataFieldsStats();
updateSelectAllCheckbox();
}
// Clear all selected fields
function clearAllSelectedFields() {
selectedFields.clear();
// Update all checkboxes
document.querySelectorAll('.field-checkbox').forEach(checkbox => {
checkbox.checked = false;
checkbox.closest('tr').classList.remove('selected');
});
updateSelectedFieldsDisplay();
updateDataFieldsStats();
updateSelectAllCheckbox();
}
// Clear only filtered fields
function clearAllFilteredFields() {
filteredDataFields.forEach(field => {
selectedFields.delete(field.id);
const row = document.querySelector(`tr[data-field-id="${field.id}"]`);
if (row) {
const checkbox = row.querySelector('.field-checkbox');
checkbox.checked = false;
row.classList.remove('selected');
}
});
updateSelectedFieldsDisplay();
updateDataFieldsStats();
updateSelectAllCheckbox();
}
// Update the select all checkbox state
function updateSelectAllCheckbox() {
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
if (!selectAllCheckbox) return;
const allFilteredSelected = filteredDataFields.length > 0 &&
filteredDataFields.every(field => selectedFields.has(field.id));
selectAllCheckbox.checked = allFilteredSelected;
selectAllCheckbox.indeterminate = !allFilteredSelected &&
filteredDataFields.some(field => selectedFields.has(field.id));
}
// Display dataset description
function displayDatasetDescription(description) {
console.log('🎨 Displaying dataset description:', description);
// Check if dataset description element already exists
let descriptionElement = document.getElementById('datasetDescription');
if (!descriptionElement) {
console.log('📌 Creating new dataset description element');
// Create the element if it doesn't exist
const tableContainer = document.getElementById('tableContainer');
const dataFieldsControls = tableContainer.querySelector('.data-fields-controls');
// Create a new div for the dataset description
descriptionElement = document.createElement('div');
descriptionElement.id = 'datasetDescription';
descriptionElement.className = 'dataset-description';
descriptionElement.style.cssText = `
padding: 15px;
background: #e8f5e9;
border: 1px solid #4caf50;
border-radius: 4px;
margin-bottom: 15px;
font-size: 14px;
line-height: 1.5;
`;
// Insert it before the controls
tableContainer.insertBefore(descriptionElement, dataFieldsControls);
console.log('✅ Dataset description element created and inserted');
} else {
console.log('📌 Using existing dataset description element');
}
// Update the content
descriptionElement.innerHTML = `
Dataset Description:
${description}
`;
console.log('✅ Dataset description content updated');
}