No findings yet. Add data in the configuration tab.
`;
return;
}
const table = document.createElement('table');
table.className = 'w-full text-sm text-left text-slate-500';
table.innerHTML = `
Date
Finding
Category
Source
Actions
`;
const tbody = table.querySelector('tbody');
// Sort by date descending
const sortedData = [...researchData].sort((a, b) => new Date(b.date) - new Date(a.date));
sortedData.forEach(item => {
const row = tbody.insertRow();
row.className = 'bg-white border-b hover:bg-slate-50';
row.innerHTML = `
${formatDate(item.date)}
${item.finding}
${item.category}
${item.source}
Edit
Delete
`;
});
dataDisplayArea.innerHTML = '';
dataDisplayArea.appendChild(table);
};
const updateCategoryChart = () => {
if (categoryChartInstance) {
categoryChartInstance.destroy();
}
const categoryCounts = researchData.reduce((acc, item) => {
acc[item.category] = (acc[item.category] || 0) + 1;
return acc;
}, {});
const labels = Object.keys(categoryCounts);
const data = Object.values(categoryCounts);
if (labels.length === 0 && categoryChartCtx) {
// Clear canvas if no data
categoryChartCtx.clearRect(0, 0, categoryChartCtx.canvas.width, categoryChartCtx.canvas.height);
return;
}
if (!categoryChartCtx) return;
categoryChartInstance = new Chart(categoryChartCtx, {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
label: 'Findings',
data: data,
backgroundColor: [
'rgba(79, 70, 229, 0.7)', 'rgba(219, 39, 119, 0.7)', 'rgba(14, 165, 233, 0.7)',
'rgba(245, 158, 11, 0.7)', 'rgba(34, 197, 94, 0.7)', 'rgba(139, 92, 246, 0.7)'
],
borderColor: '#ffffff',
borderWidth: 2,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { position: 'bottom' } }
}
});
};
const switchTab = (newTab) => {
activeTab = newTab;
if (activeTab === 'dashboard') {
contentDashboard.classList.remove('hidden');
contentConfig.classList.add('hidden');
tabDashboard.classList.add('active');
tabConfig.classList.remove('active');
prevBtn.disabled = true;
nextBtn.disabled = false;
} else {
contentDashboard.classList.add('hidden');
contentConfig.classList.remove('hidden');
tabDashboard.classList.remove('active');
tabConfig.classList.add('active');
prevBtn.disabled = false;
nextBtn.disabled = true;
}
};
const handleFormSubmit = (e) => {
e.preventDefault();
const id = findingIdInput.value ? parseInt(findingIdInput.value) : Date.now();
const newFinding = {
id: id,
source: findingSourceInput.value,
finding: findingTextInput.value,
category: findingCategoryInput.value,
date: findingDateInput.value
};
projectTitle = projectTitleInput.value || "Untitled Research Project";
if (findingIdInput.value) { // Update existing
const index = researchData.findIndex(item => item.id === id);
researchData[index] = newFinding;
} else { // Add new
researchData.push(newFinding);
}
resetForm();
renderDashboard();
switchTab('dashboard');
};
const resetForm = () => {
dataEntryForm.reset();
findingIdInput.value = '';
projectTitleInput.value = projectTitle;
formTitle.textContent = 'Add a New Finding';
submitFindingBtn.textContent = 'Add Finding';
submitFindingBtn.classList.replace('bg-blue-600', 'bg-green-600');
submitFindingBtn.classList.replace('hover:bg-blue-700', 'hover:bg-green-700');
cancelEditBtn.classList.add('hidden');
};
const handleTableClick = (e) => {
const target = e.target;
const id = parseInt(target.dataset.id);
if (target.classList.contains('delete-btn')) {
if (confirm('Are you sure you want to delete this finding?')) {
researchData = researchData.filter(item => item.id !== id);
renderDashboard();
}
}
if (target.classList.contains('edit-btn')) {
const itemToEdit = researchData.find(item => item.id === id);
if (itemToEdit) {
projectTitleInput.value = projectTitle;
findingSourceInput.value = itemToEdit.source;
findingTextInput.value = itemToEdit.finding;
findingCategoryInput.value = itemToEdit.category;
findingDateInput.value = itemToEdit.date;
findingIdInput.value = itemToEdit.id;
formTitle.textContent = 'Edit Finding';
submitFindingBtn.textContent = 'Update Finding';
submitFindingBtn.classList.replace('bg-green-600', 'bg-blue-600');
submitFindingBtn.classList.replace('hover:bg-green-700', 'hover:bg-blue-700');
cancelEditBtn.classList.remove('hidden');
switchTab('config');
}
}
};
// --- PDF Generation ---
const generatePDF = () => {
const { jsPDF } = window.jspdf;
if (researchData.length === 0) {
alert("Please add some data before generating a report.");
return;
}
// --- PDF Generation Template: "Research Brief" ---
const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
// Add header
const addHeader = () => {
doc.setFontSize(18);
doc.setFont('helvetica', 'bold');
doc.setTextColor(30, 41, 59);
doc.text(projectTitle, 105, 15, { align: 'center' });
doc.setFontSize(10);
doc.setTextColor(100, 116, 139);
doc.text('Research Data Brief', 105, 22, { align: 'center' });
doc.setLineWidth(0.5);
doc.line(15, 28, 195, 28);
};
const addFooter = () => {
const pageCount = doc.internal.getNumberOfPages();
for (let i = 1; i <= pageCount; i++) {
doc.setPage(i);
doc.setFontSize(9);
doc.setTextColor(150);
doc.text(`Page ${i} of ${pageCount}`, 105, 285, { align: 'center' });
doc.text(`Report Generated: ${new Date().toLocaleDateString('en-US')}`, 195, 285, { align: 'right' });
}
};
addHeader();
// Group data by category
const groupedData = researchData.reduce((acc, item) => {
(acc[item.category] = acc[item.category] || []).push(item);
return acc;
}, {});
let yPosition = 40;
for (const category in groupedData) {
const items = groupedData[category];
const tableBody = items.map(item => [formatDate(item.date), item.finding, item.source]);
const tableHeight = (items.length + 1) * 8 + 10;
if (yPosition + tableHeight > 270) {
doc.addPage();
addHeader();
yPosition = 40;
}
doc.setFontSize(14);
doc.setFont('helvetica', 'bold');
doc.setTextColor(79, 70, 229);
doc.text(category, 15, yPosition);
doc.autoTable({
head: [['Date', 'Finding', 'Source']],
body: tableBody,
startY: yPosition + 4,
theme: 'striped',
headStyles: { fillColor: [67, 56, 202] },
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: { 1: { cellWidth: 'auto' } }
});
yPosition = doc.previousAutoTable.finalY + 15;
}
addFooter();
doc.save(`${projectTitle.replace(/ /g, '_')}_Brief.pdf`);
};
// --- Event Listeners ---
tabDashboard.addEventListener('click', () => switchTab('dashboard'));
tabConfig.addEventListener('click', () => switchTab('config'));
nextBtn.addEventListener('click', () => switchTab('config'));
prevBtn.addEventListener('click', () => switchTab('dashboard'));
dataEntryForm.addEventListener('submit', handleFormSubmit);
dataDisplayArea.addEventListener('click', handleTableClick);
cancelEditBtn.addEventListener('click', resetForm);
downloadPdfBtn.addEventListener('click', generatePDF);
loadSampleBtn.addEventListener('click', () => {
projectTitle = document.getElementById('sample-title').innerText;
researchData = JSON.parse(document.getElementById('sample-data').innerText);
// Assign unique IDs to sample data
researchData.forEach(item => item.id = Date.now() + Math.random());
projectTitleInput.value = projectTitle;
renderDashboard();
switchTab('dashboard');
});
// --- Initializer ---
const initializeTool = () => {
loadSampleBtn.click(); // Load sample data on start
};
initializeTool();
});