Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 25 additions & 21 deletions js/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const STORAGE_SAVE_KEY = "launchdesk-v1-items";
const STORAGE_LOAD_KEY = "launchdesk-items-v1"; // Intentional bug: this key should match STORAGE_SAVE_KEY.
const STORAGE_KEY = "launchdesk-v1-items";

const demoChecks = [
{
Expand Down Expand Up @@ -91,7 +90,7 @@ const activityLog = document.getElementById("activityLog");
let checks = loadChecks();
let currentView = checks;

form.addEventListener("submit", (event) => handleAddChek(event)); // Intentional bug: misspelled function name.
form.addEventListener("submit", (event) => handleAddCheck(event));
searchInput.addEventListener("input", applyFilters);
statusFilter.addEventListener("change", applyFilters);
priorityFilter.addEventListener("change", applyFilters);
Expand All @@ -104,7 +103,7 @@ renderApp();
logActivity("Demo data loaded. Start by testing the checklist workflows.");

function loadChecks() {
const saved = localStorage.getItem(STORAGE_LOAD_KEY);
const saved = localStorage.getItem(STORAGE_KEY);

if (!saved) {
return [...demoChecks];
Expand All @@ -119,7 +118,7 @@ function loadChecks() {
}

function saveChecks() {
localStorage.setItem(STORAGE_SAVE_KEY, JSON.stringify(checks));
localStorage.setItem(STORAGE_KEY, JSON.stringify(checks));
}

function handleAddCheck(event) {
Expand All @@ -132,8 +131,7 @@ function handleAddCheck(event) {
const owner = ownerInput.value.trim() || "Unassigned";
const dueDate = dueDateInput.value || new Date().toISOString().slice(0, 10);

if (!title && !category) {
// Intentional bug: validation should stop when either required field is missing.
if (!title || !category) {
formMessage.textContent =
"Please enter a check title and choose a category.";
return;
Expand Down Expand Up @@ -164,12 +162,16 @@ function applyFilters() {
const selectedPriority = priorityFilter.value;

let filtered = checks.filter((check) =>
check.owner.toLowerCase().includes(searchTerm),
); // Intentional bug: search should include title, category, priority, status, and owner.
check.owner.toLowerCase().includes(searchTerm) ||
check.title.toLowerCase().includes(searchTerm) ||
check.category.toLowerCase().includes(searchTerm) ||
check.priority.toLowerCase().includes(searchTerm) ||
check.status.toLowerCase().includes(searchTerm),
);

if (selectedStatus !== "All") {
filtered = filtered.filter((check) => check.priority === selectedStatus);
} // Intentional bug: status filter compares against priority.
filtered = filtered.filter((check) => check.status === selectedStatus);
}

if (selectedPriority !== "All") {
filtered = filtered.filter((check) => check.priority === selectedPriority);
Expand All @@ -191,8 +193,7 @@ function renderRows(list) {

const rows = list.map((check) => {
const priorityClass = `priority-${check.priority.toLowerCase()}`;
const statusClass = `status-${check.status.toLowerCase()}`; // Intentional bug: "In Progress" needs a slug class.

const statusClass = `status-${check.status.toLowerCase().replaceAll(" ", "-")}`;
return `
<tr>
<td>
Expand Down Expand Up @@ -231,11 +232,14 @@ function renderRows(list) {

function updateMetrics() {
const total = checks.length;
const fixed = checks.filter((check) => check.status === "Complete").length; // Intentional bug: valid fixed status is "Fixed".
const fixed = checks.filter((check) => check.status === "Fixed").length;
const criticalOpen = checks.filter(
(check) => check.priority === "Critical" && check.status !== "Fixed",
).length;
const dueSoon = checks.filter((check) => daysUntil(check.dueDate) > 7).length; // Intentional bug: this should count items due within 7 days.
const dueSoon = checks.filter((check) => {
const days = daysUntil(check.dueDate);
return days >= 0 && days <= 7;
}).length;
const score = total === 0 ? 0 : Math.round((fixed / total) * 100);

totalCount.textContent = total;
Expand All @@ -247,13 +251,13 @@ function updateMetrics() {
}

function handleTableClick(event) {
const deleteButton = event.target.closest("[data-delete-id]"); // Intentional bug: button uses data-remove-id.
const deleteButton = event.target.closest("[data-remove-id]");

if (!deleteButton) {
return;
}

const id = Number(deleteButton.dataset.deleteId);
const id = Number(deleteButton.dataset.removeId);
const removed = checks.find((check) => check.id === id);
checks = checks.filter((check) => check.id !== id);
saveChecks();
Expand All @@ -276,16 +280,16 @@ function handleStatusChange(event) {
}

check.status = statusSelect.value;
renderRows(currentView);
saveChecks();
applyFilters();
logActivity(`Changed "${check.title}" to ${check.status}.`);
// Intentional bug: status changes should save, update filters, and refresh metrics.
}

async function resetDemoData() {
formMessage.textContent = "";

try {
const response = await fetch("data/launch-seed.json"); // Intentional bug: real file is data/launch-checks.json.
const response = await fetch("data/launch-checks.json");

if (!response.ok) {
throw new Error(`Demo data request failed with ${response.status}`);
Expand All @@ -312,7 +316,7 @@ function exportCsv() {
"Due Date",
];
const rows = currentView.map((check) => [
check.name, // Intentional bug: property should be check.title.
check.title,
check.category,
check.priority,
check.status,
Expand Down