Use the controls to generate your first analysis.
`;
document.getElementById('pdf-download-wrapper').style.display = 'none';
};
const generateAnalysis = () => {
const dimension = groupBySelect.value;
const metric = metricSelect.value;
const aggFunc = document.getElementById('agg-func').value;
if (!dimension || !metric) {
alert("Please select both a dimension and a metric.");
return;
}
const groupedData = rawData.reduce((acc, row) => {
const key = row[dimension];
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(row[metric]);
return acc;
}, {});
const labels = Object.keys(groupedData);
let data = [];
let tableData = [];
labels.forEach(label => {
const values = groupedData[label].filter(v => v !== null); // Filter out nulls for calculations
let result;
switch(aggFunc) {
case 'sum':
result = values.reduce((a, b) => a + b, 0);
break;
case 'average':
result = values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : 0;
break;
case 'count':
result = groupedData[label].length; // Count all, including nulls
break;
case 'countUnique':
result = new Set(values).size;
break;
}
data.push(result);
tableData.push({ dimension: label, value: result });
});
analysisResult = {
title: `${aggFunc.charAt(0).toUpperCase() + aggFunc.slice(1)} of ${metric} by ${dimension}`,
labels,
data,
tableData,
dimensionHeader: dimension,
metricHeader: `${aggFunc.charAt(0).toUpperCase() + aggFunc.slice(1)}(${metric})`
};
renderResults(analysisResult);
};
const renderResults = (result) => {
analysisTitle.textContent = result.title;
// Prepare display area
resultsDisplay.innerHTML = `
`;
// Render Chart
const chartType = document.getElementById('chart-type').value;
const ctx = document.getElementById('analysisChart').getContext('2d');
if (currentChart) {
currentChart.destroy();
}
currentChart = new Chart(ctx, {
type: chartType,
data: {
labels: result.labels,
datasets: [{
label: result.title,
data: result.data,
backgroundColor: chartType === 'pie' ?
result.data.map((_, i) => `hsl(${i * 360 / result.data.length}, 70%, 60%)`) :
'rgba(0, 123, 255, 0.6)',
borderColor: 'rgba(0, 123, 255, 1)',
borderWidth: 1
}]
},
options: { responsive: true, maintainAspectRatio: false }
});
// Render Table
const table = document.getElementById('analysis-table');
table.innerHTML = `
| ${result.dimensionHeader} |
${result.metricHeader} |
${result.tableData.map(row => `
| ${row.dimension} |
${typeof row.value === 'number' ? row.value.toLocaleString(undefined, { maximumFractionDigits: 2 }) : row.value} |
`).join('')}
`;
document.getElementById('pdf-download-wrapper').style.display = 'block';
};
const downloadPDF = () => {
if (!analysisResult.title) {
alert("Please generate an analysis before downloading.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.setFontSize(20);
doc.text("Ad Hoc Analysis Report", 105, 20, { align: 'center' });
doc.setFontSize(14);
doc.text(analysisResult.title, 14, 35);
let finalY = 40;
if(currentChart) {
try {
const chartImg = currentChart.toBase64Image('image/png', 1.0);
doc.addImage(chartImg, 'PNG', 14, finalY, 180, 100);
finalY += 110;
} catch(e) { console.error("Could not add chart to PDF:", e); }
}
const tableHead = [analysisResult.dimensionHeader, analysisResult.metricHeader];
const tableBody = analysisResult.tableData.map(row => [row.dimension, row.value.toLocaleString(undefined, { maximumFractionDigits: 2 })]);
doc.autoTable({
startY: finalY,
head: [tableHead],
body: tableBody,
theme: 'striped'
});
doc.save(`AdHoc_Analysis_${new Date().toISOString().slice(0,10)}.pdf`);
};
// --- EVENT LISTENERS ---
processDataBtn.addEventListener('click', processData);
generateAnalysisBtn.addEventListener('click', generateAnalysis);
downloadPdfBtn.addEventListener('click', downloadPDF);
window.adhocSwitchTab = (evt, tabName) => {
if (evt.currentTarget.classList.contains('disabled')) return;
document.querySelectorAll('#adhoc-analysis-container .adhoc-tab-content').forEach(tc => tc.style.display = 'none');
document.querySelectorAll('#adhoc-analysis-container .adhoc-tab-button').forEach(tl => tl.classList.remove('active'));
document.getElementById(tabName).style.display = 'block';
evt.currentTarget.classList.add('active');
};
});