No techniques in the library yet.
';
return;
}
techniques.forEach(tech => {
const item = document.createElement('div');
item.className = 'pts-list-item';
item.innerHTML = `
${escapeHtml(tech.name)}
`;
configListContainer.appendChild(item);
});
}
/** Handles filter changes and updates results */
window.ptsApplyFilters = () => {
// Use try-catch for safety, though querySelector shouldn't fail if HTML is correct
try {
// Get selected radio values
const timeChecked = document.querySelector('input[name="time"]:checked');
const styleChecked = document.querySelector('input[name="style"]:checked');
const goalChecked = document.querySelector('input[name="goal"]:checked');
currentFilters.time = timeChecked ? timeChecked.value : 'any';
currentFilters.style = styleChecked ? styleChecked.value : 'any';
currentFilters.goal = goalChecked ? goalChecked.value : 'any';
renderResults();
} catch (error) {
console.error("Error applying filters:", error);
showMessage("Error updating results based on filters.", true);
}
}
/** Adds a new technique */
window.ptsAddTechnique = () => {
// Null checks already handled by initial check
if (!newNameInput || !newDescInput || !addForm) return;
const name = newNameInput.value.trim();
const description = newDescInput.value.trim();
const selectedTags = Array.from(addForm.querySelectorAll('input[name="tags"]:checked')).map(cb => cb.value);
if (!name || !description) {
showMessage("Please enter both a name and description.", true); return;
}
if (techniques.some(t => t.name.toLowerCase() === name.toLowerCase())) {
showMessage("A technique with this name already exists.", true); return;
}
techniques.push({
id: 't' + Date.now(),
name,
description,
tags: selectedTags
});
addForm.reset(); // Clear form
showMessage(null); // Clear errors
renderConfigList(); // Update config view
ptsApplyFilters(); // Update dashboard view in case it matches current filters
};
/** Deletes a technique */
window.ptsDeleteTechnique = (id) => {
if (!confirm('Are you sure you want to delete this technique?')) return;
techniques = techniques.filter(t => t.id !== id);
renderConfigList();
ptsApplyFilters(); // Update dashboard view
};
/** Switches tabs */
window.ptsShowTab = (tabId, element) => {
// Null checks already handled by initial check
if (!container || !tabLinks) return;
container.querySelectorAll('.pts-tab-content').forEach(tab => tab.classList.remove('pts-active'));
container.querySelectorAll('.pts-tab-link').forEach(link => link.classList.remove('pts-active'));
const tabToShow = container.querySelector('#' + tabId);
if (tabToShow) tabToShow.classList.add('pts-active');
if (element) element.classList.add('pts-active');
currentTabId = tabId;
updateNavButtons();
};
/** Handles nav buttons */
window.ptsNavigateTabs = (isNext) => {
// Null checks already handled by initial check
if (!container || !tabLinks) return;
const targetTabId = isNext ? 'pts-tab-config' : 'pts-tab-dashboard';
const targetTabLink = container.querySelector(`.pts-tab-link[onclick*="'${targetTabId}'"]`);
if(targetTabLink) targetTabLink.click();
};
/** Updates nav button states */
function updateNavButtons() {
// Null checks already handled by initial check
if (!prevBtn || !nextBtn) return;
prevBtn.disabled = currentTabId === 'pts-tab-dashboard';
nextBtn.disabled = currentTabId === 'pts-tab-config';
}
/** Downloads the filtered results as PDF */
function downloadPDF() {
if (typeof jsPDF === 'undefined' || !jsPDF.autoTable) { showMessage("PDF library not loaded.", true); return; }
if (filteredResults.length === 0) { showMessage("No recommendations to download based on current filters.", true); return; }
showLoader(true);
showMessage(null);
try {
const doc = new jsPDF();
const pageMargin = 40;
const pageWidth = doc.internal.pageSize.getWidth();
let y = pageMargin;
// 1. Title
doc.setFontSize(18);
doc.setFont(undefined, 'bold');
doc.text("Productivity Technique Recommendations", pageWidth / 2, y, { align: 'center' });
y += 30;
doc.setFont(undefined, 'normal');
// 2. Filters Used (Optional but helpful context)
doc.setFontSize(10);
doc.text(`Filters Applied: Time(${currentFilters.time}), Style(${currentFilters.style}), Goal(${currentFilters.goal})`, pageMargin, y);
y += 20;
// 3. Results Table
const tableHead = [["Technique", "Description", "Tags"]];
const tableBody = filteredResults.map(tech => [
tech.name,
tech.description,
tech.tags.map(tag => tag.replace(':', ': ').replace(/^(time|style|goal)/, match => match.charAt(0).toUpperCase() + match.slice(1))).join(', ')
]);
doc.autoTable({
startY: y,
head: tableHead,
body: tableBody,
theme: 'striped',
headStyles: { fillColor: [0, 95, 204] }, // Primary color
styles: { fontSize: 9, cellPadding: 4, valign: 'top' },
columnStyles: {
0: { cellWidth: 'auto' }, // Name
1: { cellWidth: '*' }, // Description (wrap)
2: { cellWidth: 'auto' } // Tags
},
didParseCell: function (data) {
// Make description wrap
if (data.column.index === 1) { data.cell.styles.cellWidth = 'wrap'; }
if (data.column.index === 2) { data.cell.styles.fontSize = 8; } // Smaller font for tags
}
});
doc.save("Productivity_Techniques.pdf");
} catch (e) {
console.error("Error generating PDF:", e);
showMessage("An error occurred while generating the PDF.", true);
} finally {
showLoader(false);
}
}
/** Basic HTML escaping */
function escapeHtml(unsafe) {
if (typeof unsafe !== 'string') return unsafe ?? ''; // Handle null/undefined
return unsafe
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
/** Shows or hides loader */
function showLoader(show) {
// Null checks already handled by initial check
if (!loaderOverlay) return;
loaderOverlay.style.display = show ? 'flex' : 'none';
}
/** Displays messages */
function showMessage(message, isError = false) {
// Null checks already handled by initial check
if (!messageArea) return;
if (!message) { messageArea.style.display = 'none'; return; }
messageArea.textContent = message;
messageArea.className = `mt-4 text-center font-medium ${isError ? 'text-red-600' : 'text-blue-600'}`;
messageArea.style.display = 'block';
// Auto-hide message after a few seconds
setTimeout(() => { if(messageArea.textContent === message) { messageArea.style.display = 'none'; } }, 4000);
}
// === Event Listeners ===
// Null check button before adding listener
if (downloadBtn) {
downloadBtn.addEventListener('click', downloadPDF);
}
// === Initial Render ===
renderConfigList();
ptsApplyFilters(); // Initial render of dashboard results
updateNavButtons();
});