Bug deleted.
';
// But for this spec, we'll filter and re-render.
// bugs = bugs.filter((bug) => bug.id !== bugId);
// renderDashboard();
}
}
}
/**
* Handles edits made directly to cards (inputs, textareas, selects)
* @param {Event} e - The input or change event
*/
function handleCardUpdate(e) {
const field = e.target.dataset.field;
if (!field) return; // Not an editable field
const card = e.target.closest(".bft-bug-card");
if (!card) return;
const bugId = card.dataset.id;
const bug = findBugById(bugId);
if (!bug) return;
// Update the bug object in our state
bug[field] = e.target.value;
// Special case: If priority changed, update border
if (field === 'priority') {
card.className = `bft-bug-card priority-${bug.priority}`;
}
// Special case: If status changed, move the card
if (field === "status") {
// A simple re-render will move it
renderDashboard();
}
}
/**
* Generates a PDF report of all bugs
*/
function downloadPDF() {
if (
typeof window.jspdf === "undefined" ||
typeof window.jspdf.jsPDF.autoTable === "undefined"
) {
console.error("jsPDF or jsPDF-AutoTable is not loaded.");
// Use a simple, non-blocking modal for this.
alert("Error: PDF library could not be loaded. Please check your internet connection.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF("p", "pt", "a4");
const tableHead = [
"ID",
"Title",
"Status",
"Priority",
"Assignee",
"Description",
];
const tableBody = bugs.map((bug) => [
bug.id.substring(0, 10), // Shorten ID for PDF
bug.title,
bug.status,
bug.priority,
bug.assignee,
bug.desc,
]);
doc.setFontSize(18);
doc.text("Bug Tracker Report", 40, 40);
doc.autoTable({
startY: 50,
head: tableHead,
body: tableBody,
theme: "striped",
headStyles: {
fillColor: [0, 115, 230],
textColor: [255, 255, 255],
},
columnStyles: {
0: { cellWidth: 60 },
1: { cellWidth: 100 },
2: { cellWidth: 50 },
3: { cellWidth: 50 },
4: { cellWidth: 70 },
5: { cellWidth: "auto" },
},
});
doc.save("Bug_Tracker_Report.pdf");
}
/**
* Switches between tabs
* @param {string} targetTabId - The ID of the tab pane to show
*/
function showTab(targetTabId) {
if (!targetTabId) return;
const targetPane = bftContainer.querySelector("#" + targetTabId);
const targetButton = bftContainer.querySelector(
`.bft-tab-btn[data-target="${targetTabId}"]`
);
if (!targetPane || !targetButton) return;
tabPanes.forEach((pane) => pane.classList.remove("active"));
tabButtons.forEach((btn) => btn.classList.remove("active"));
targetPane.classList.add("active");
targetButton.classList.add("active");
}
/**
* Helper to escape HTML to prevent XSS
*/
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));
});
// Form Listener
if (configForm) {
configForm.addEventListener("submit", addNewBug);
}
// Dashboard Listeners (Event Delegation)
if (dashboardColumns) {
dashboardColumns.addEventListener("click", handleDashboardClick);
dashboardColumns.addEventListener("change", handleCardUpdate); // For