Add excerpts using the "Entry Configuration" tab to populate this log.
Add New Data Excerpt
Add excerpts using the "Entry Configuration" tab to populate this log.
`;
return;
}
logEntries.forEach(entry => {
dashboardList.appendChild(createDashboardItem(entry));
});
// Setup listeners for newly created elements
setupDashboardListeners();
}
/**
* Creates an editable item card for the dashboard
*/
function createDashboardItem(entry) {
const card = document.createElement("div");
card.className = "qdel-entry-card";
card.dataset.id = entry.id;
// Card structure with editable fields
card.innerHTML = `
ID/Code
Source
Theme/Code
`;
return card;
}
/**
* Attaches listeners to the dashboard elements
*/
function setupDashboardListeners() {
if (!dashboardList) return;
// Listen for all input/change events to update state
dashboardList.querySelectorAll('input, textarea').forEach(element => {
element.addEventListener('change', handleDashboardUpdate);
element.addEventListener('input', handleDashboardUpdate);
});
// Listen for removal clicks (delegation)
dashboardList.querySelectorAll('button[data-action="remove"]').forEach(button => {
button.addEventListener('click', handleDashboardRemove);
});
}
/**
* Updates the master `logEntries` array when dashboard fields change
*/
function handleDashboardUpdate(e) {
const card = e.target.closest(".qdel-entry-card");
if (!card) return;
const entryId = card.dataset.id;
const entry = logEntries.find(e => e.id === entryId);
if (!entry) return;
const field = e.target.dataset.field;
if (!field) return;
// Update the specific field in the master array
entry[field] = e.target.value;
// Special case: Ensure ID is uppercase
if (field === 'code') entry.code = entry.code.toUpperCase();
}
/**
* Handles removing an item directly from the dashboard
*/
function handleDashboardRemove(e) {
const card = e.target.closest(".qdel-entry-card");
if (card) {
// Remove from master array
logEntries = logEntries.filter(entry => entry.id !== card.dataset.id);
renderDashboard(); // Re-render dashboard
}
}
/**
* Generates a PDF report from the dashboard data
*/
function downloadPDF() {
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF.autoTable === 'undefined') {
alert("Error: PDF libraries could not be loaded. Please try again.");
return;
}
if (logEntries.length === 0) {
alert("Log is empty. Please add entries first.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF("l", "pt", "a4"); // Landscape for more space
const margin = 40;
let yPos = margin;
const tableHead = [["ID", "Source", "Excerpt Text", "Theme/Code", "Analyst Notes"]];
const tableBody = [];
// Get data from the *current* master state (which reflects dashboard edits)
logEntries.forEach((entry) => {
tableBody.push([
entry.code,
entry.source,
entry.excerpt,
entry.theme,
entry.notes
]);
});
doc.setFontSize(18);
doc.text("Qualitative Data Excerpt Log", margin, yPos);
yPos += 30;
doc.autoTable({
startY: yPos,
head: tableHead,
body: tableBody,
theme: 'striped',
headStyles: {
fillColor: [0, 115, 230], // Blue
textColor: [255, 255, 255],
fontSize: 10
},
columnStyles: {
0: { cellWidth: 60 }, // ID
1: { cellWidth: 70 }, // Source
2: { cellWidth: 200 }, // Excerpt (widest)
3: { cellWidth: 70 }, // Theme
4: { cellWidth: 'auto' }, // Notes (rest of space)
},
styles: {
fontSize: 9,
cellPadding: 3,
overflow: 'linebreak' // Important for long text
},
margin: { left: margin, right: margin }
});
doc.save("Qualitative_Excerpt_Log.pdf");
}
/**
* Helper to escape HTML
*/
function escapeHTML(str) {
if (!str) return "";
return str
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// --- 3. INITIALIZATION & EVENT LISTENERS ---
// Tab Listeners
tabButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
navButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
// Config Tab Listeners
if (addEntryForm) {
addEntryForm.addEventListener("submit", handleAddEntry);
}
// Dashboard Tab Listeners
if (pdfBtn) {
pdfBtn.addEventListener("click", downloadPDF);
}
// Load sample data
logEntries = [
{
id: 'entry-1',
code: 'P01-Q05',
source: 'Interview 1',
excerpt: '“I think the biggest hurdle for adoption was just trust. People are used to the old system, and the new one felt too opaque, too much like a black box, even though it was technically more secure.”',
theme: 'Trust/Adoption Barriers',
notes: 'Participant is a senior manager; suggests internal communication failed to bridge the trust gap despite technical superiority.'
},
{
id: 'entry-2',
code: 'FG2-T04',
source: 'Focus Group 2',
excerpt: 'Observed several participants nodding when Lisa mentioned the cost saving, but Jane immediately countered with the time loss. Conflict between efficiency and cost.',
theme: 'Efficiency vs. Cost',
notes: 'Non-verbal observation is key here. Note the quick shift from agreement to explicit disagreement in the discussion.'
}
];
// Initial State
renderDashboard();
showTab("qdel-tab-dashboard");
});