Research Data Organization Tool

Research Data Organization Tool

Structure, analyze, and report your qualitative data findings.

Total Findings

0

Unique Categories

0

Most Recent Entry

-

Research Findings

Findings per Category

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} `; }); 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(); });
Scroll to Top