Skip to content

fix: resolve all 12 debugging bugs in launch checklist app#2

Open
arunod-wijesena wants to merge 15 commits intocodezelaca:mainfrom
arunod-wijesena:main
Open

fix: resolve all 12 debugging bugs in launch checklist app#2
arunod-wijesena wants to merge 15 commits intocodezelaca:mainfrom
arunod-wijesena:main

Conversation

@arunod-wijesena
Copy link
Copy Markdown

@arunod-wijesena arunod-wijesena commented May 8, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Search now matches title, category, priority, status, and owner; status filtering works correctly
    • Status badge styling fixed; status changes now persist, reapply filters, refresh metrics, and log activity
    • Add-item form validation and submit fixed; delete actions work reliably
    • Demo reset loads correct demo data; CSV export includes item title
    • Metrics and due-soon calculations corrected
  • Chores

    • Persistence reload key corrected and saved data loading/parsing made more robust

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7da69976-8fd3-4ff2-8507-f716ad65c46f

📥 Commits

Reviewing files that changed from the base of the PR and between 47c207c and 399090d.

📒 Files selected for processing (1)
  • js/app.js

📝 Walkthrough

Walkthrough

Fixes in js/app.js: align storage key, wire and validate add-check form, tighten load/save parsing, expand filtering and slugify status classes, persist status changes and refresh metrics/views, correct delete handling, fix demo JSON path and CSV export, and reorganize helper/comment blocks.

Changes

Launch Checklist Bug Fixes

Layer / File(s) Summary
Data Persistence
js/app.js
Local storage load key is set to "launchdesk-v1-items" to match the save key.
Form Input & Validation
js/app.js
Form submit wired to handleAddCheck; loadChecks() JSON parsing tightened and documented; saveChecks() documented; handleAddCheck requires both title and category.
Filtering & Display
js/app.js
applyFilters() searches title, category, priority, status, and owner; status comparison fixed; status dropdown rendering refactored via .map(...).join(""); status badge classes are slugified (e.g., "In Progress" -> in-progress).
Status Change Workflow
js/app.js
On status change: save via saveChecks(), reapply applyFilters(), call updateMetrics(), and log the change.
Metrics Calculation
js/app.js
fixed counted as status === "Fixed"; criticalOpen = Critical items excluding Fixed; dueSoon = items due within 7 days inclusive using daysUntil.
Table Event Handlers
js/app.js
Delete handler locates [data-remove-id], reads the dataset key, removes the matching item, persists, reapplies filters, and logs.
Data Loading & Export
js/app.js
Demo reset fetch requests data/launch-checks.json and validates the returned array. CSV export uses check.title for the first column instead of a non-existent check.name.
Activity Logging & Utilities
js/app.js
Comment blocks placed around logging and helper utilities; daysUntil and escapeHtml remain for date math and sanitization.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through keys and chased loose rows,

Fixed the filters where wild text grows.
Statuses slugged and metrics set true,
CSV sings titles, demo loads anew.
A rabbit patched the checklist through.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: resolve all 12 debugging bugs in launch checklist app' directly and clearly describes the main purpose of the changeset, which is to fix multiple bugs in the launch checklist app.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
js/app.js (2)

289-294: 💤 Low value

Redundant updateMetrics() call.

applyFilters() already calls renderApp() which calls updateMetrics(). The explicit call at line 292 results in metrics being computed twice.

♻️ Remove redundant call
   check.status = statusSelect.value;
   saveChecks();
   applyFilters();
-  updateMetrics();
   logActivity(`Changed "${check.title}" to ${check.status}.`);
-
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/app.js` around lines 289 - 294, The code sets check.status, calls
saveChecks(), then calls applyFilters() and updateMetrics(), but updateMetrics()
is redundant because applyFilters() -> renderApp() already calls
updateMetrics(); remove the explicit updateMetrics() invocation after
applyFilters() so metrics are only computed once (leave check.status assignment,
saveChecks(), applyFilters(), and logActivity(`Changed "${check.title}" to
${check.status}.`) intact).

202-202: 💤 Low value

Consider using replaceAll for consistency.

replace(" ", "-") only replaces the first space. While current statuses only have one space max, using replaceAll would be more robust and consistent with other string replacements in this file (e.g., escapeHtml at lines 384-388).

♻️ Suggested improvement
-    const statusClass = `status-${check.status.toLowerCase().replace(" ", "-")}`;
+    const statusClass = `status-${check.status.toLowerCase().replaceAll(" ", "-")}`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/app.js` at line 202, The current construction of statusClass uses
check.status.toLowerCase().replace(" ", "-") which only replaces the first
space; change this to use replaceAll so all spaces are converted consistently
(e.g., update the expression building statusClass to call replaceAll(" ", "-")
on check.status.toLowerCase()), ensuring behavior matches other replacements in
the file like escapeHtml and handles multi-word statuses robustly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@js/app.js`:
- Around line 289-294: The code sets check.status, calls saveChecks(), then
calls applyFilters() and updateMetrics(), but updateMetrics() is redundant
because applyFilters() -> renderApp() already calls updateMetrics(); remove the
explicit updateMetrics() invocation after applyFilters() so metrics are only
computed once (leave check.status assignment, saveChecks(), applyFilters(), and
logActivity(`Changed "${check.title}" to ${check.status}.`) intact).
- Line 202: The current construction of statusClass uses
check.status.toLowerCase().replace(" ", "-") which only replaces the first
space; change this to use replaceAll so all spaces are converted consistently
(e.g., update the expression building statusClass to call replaceAll(" ", "-")
on check.status.toLowerCase()), ensuring behavior matches other replacements in
the file like escapeHtml and handles multi-word statuses robustly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a47ce37-1311-41c3-9bdd-c013eeff827d

📥 Commits

Reviewing files that changed from the base of the PR and between ccb0d19 and 7059d7a.

📒 Files selected for processing (1)
  • js/app.js

@arunod-wijesena arunod-wijesena changed the title Debugging session assignment fix: check submit even listener May 8, 2026
@arunod-wijesena arunod-wijesena changed the title fix: check submit even listener fix: resolve all 12 debugging bugs in launch checklist app May 8, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
js/app.js (1)

106-119: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate persisted/fetched payload shape before assigning to checks.

JSON.parse(...) and response.json() can return non-array values. When that happens, flows using checks.filter(...)/checks.unshift(...) fail at runtime. Add an Array.isArray guard at both ingress points.

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

   if (!saved) {
     return [...demoChecks];
   }

   try {
-    return JSON.parse(saved);
+    const parsed = JSON.parse(saved);
+    return Array.isArray(parsed) ? parsed : [...demoChecks];
   } catch (error) {
     console.warn("Could not parse saved launch checks.", error);
     return [...demoChecks];
   }
 }
 async function resetDemoData() {
   formMessage.textContent = "";

   try {
     const response = await fetch("data/launch-checks.json");

     if (!response.ok) {
       throw new Error(`Demo data request failed with ${response.status}`);
     }

-    checks = await response.json();
+    const data = await response.json();
+    if (!Array.isArray(data)) {
+      throw new Error("Demo data payload is not an array.");
+    }
+    checks = data;
     saveChecks();
     applyFilters();
     logActivity("Demo checklist reloaded from JSON.");
   } catch (error) {

Also applies to: 309-317

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/app.js` around lines 106 - 119, The parsed payload from localStorage in
loadChecks (STORAGE_LOAD_KEY) and the payload returned by response.json() must
be validated as arrays before assigning to checks to avoid runtime errors when
calling checks.filter or checks.unshift; update loadChecks to JSON.parse(saved)
then use Array.isArray(...) to return the parsed value only if it's an array
(otherwise return [...demoChecks]) and apply the same pattern where you consume
response.json() (the fetch/response handling that assigns to checks) so that
non-array responses fall back to [...demoChecks] and do not replace checks with
a non-array value.
🧹 Nitpick comments (1)
js/app.js (1)

206-206: ⚡ Quick win

Make status class slugging fully whitespace-safe.

At Line 206, .replace(" ", "-") only replaces the first space. Using a global whitespace replacement makes class naming stable for any multi-word status.

Proposed refactor
-    const statusClass = `status-${check.status.toLowerCase().replace(" ", "-")}`;
+    const statusClass = `status-${String(check.status ?? "")
+      .trim()
+      .toLowerCase()
+      .replace(/\s+/g, "-")}`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/app.js` at line 206, The class generation for statusClass uses
check.status.toLowerCase().replace(" ", "-") which only replaces the first
space; update it to replace all whitespace (e.g., use a global regex like /\s+/g
or replaceAll for spaces) so multi-word check.status values are slugged
consistently, ensuring statusClass is built from check.status with all
whitespace replaced by hyphens.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@js/app.js`:
- Around line 106-119: The parsed payload from localStorage in loadChecks
(STORAGE_LOAD_KEY) and the payload returned by response.json() must be validated
as arrays before assigning to checks to avoid runtime errors when calling
checks.filter or checks.unshift; update loadChecks to JSON.parse(saved) then use
Array.isArray(...) to return the parsed value only if it's an array (otherwise
return [...demoChecks]) and apply the same pattern where you consume
response.json() (the fetch/response handling that assigns to checks) so that
non-array responses fall back to [...demoChecks] and do not replace checks with
a non-array value.

---

Nitpick comments:
In `@js/app.js`:
- Line 206: The class generation for statusClass uses
check.status.toLowerCase().replace(" ", "-") which only replaces the first
space; update it to replace all whitespace (e.g., use a global regex like /\s+/g
or replaceAll for spaces) so multi-word check.status values are slugged
consistently, ensuring statusClass is built from check.status with all
whitespace replaced by hyphens.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b82555d5-d335-4d6b-b987-59b6d2effb69

📥 Commits

Reviewing files that changed from the base of the PR and between f454f57 and cb387f8.

📒 Files selected for processing (1)
  • js/app.js

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@js/app.js`:
- Line 137: The code uses toISOString() and new Date('YYYY-MM-DD') which
treat/produce UTC dates and cause off-by-one local-date errors; change places
that create or parse date-only values (e.g., dueDate default via
dueDateInput.value / dueDate, the parser used in the block around lines 375-392,
and any dueSoon comparisons) to construct dates in local time by splitting the
YYYY-MM-DD string into year/month/day and using new Date(year, month-1, day)
(and when defaulting build today's YYYY-MM-DD from new Date() local values), and
ensure any comparisons normalize to local-midnight Date objects so due dates and
dueSoon logic use local calendar days instead of UTC.
- Around line 115-116: The code assigns JSON-parsed data to checks using only
Array.isArray(parsed) which is unsafe; update the logic where parsed is used
(the block returning parsed vs [...demoChecks], and the similar block at lines
~316-318) to validate each item is an object and has string fields "priority",
"status", and "dueDate" (and any other required checklist properties) before
accepting it—e.g., filter or map parsed to include only items that pass typeof
checks and required key presence, and if validation fails for the array, fall
back to demoChecks or call resetDemoData(); reference the parsed variable and
the resetDemoData handling so malformed localStorage/demo payloads never get
assigned to checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 20e27cac-c826-4d7e-bbe9-08c230f0da05

📥 Commits

Reviewing files that changed from the base of the PR and between cb387f8 and 47c207c.

📒 Files selected for processing (1)
  • js/app.js

Comment thread js/app.js
Comment on lines +115 to +116
const parsed = JSON.parse(saved);
return Array.isArray(parsed) ? parsed : [...demoChecks]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate checklist items before assigning them to checks.

Array.isArray(parsed) is too weak here, and resetDemoData() accepts any JSON payload. The render path later assumes every item has string priority/status/dueDate fields, so malformed localStorage or a bad demo file can still take down the app on the next render.

Suggested hardening
+function cloneDemoChecks() {
+  return demoChecks.map((check) => ({ ...check }))
+}
+
+function isValidCheck(check) {
+  return (
+    check &&
+    Number.isFinite(check.id) &&
+    typeof check.title === "string" &&
+    typeof check.category === "string" &&
+    typeof check.priority === "string" &&
+    typeof check.status === "string" &&
+    typeof check.owner === "string" &&
+    typeof check.dueDate === "string" &&
+    typeof check.notes === "string"
+  )
+}
+
+function normalizeChecks(value) {
+  return Array.isArray(value) && value.every(isValidCheck)
+    ? value
+    : cloneDemoChecks()
+}
+
 function loadChecks() {
   const saved = localStorage.getItem(STORAGE_LOAD_KEY);
@@
   try {
     const parsed = JSON.parse(saved);
-    return Array.isArray(parsed) ? parsed : [...demoChecks]
+    return normalizeChecks(parsed)
   } catch (error) {
     console.warn("Could not parse saved launch checks.", error);
-    return [...demoChecks];
+    return cloneDemoChecks();
   }
 }
@@
-    checks = await response.json();
+    checks = normalizeChecks(await response.json());

Also applies to: 316-318

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/app.js` around lines 115 - 116, The code assigns JSON-parsed data to
checks using only Array.isArray(parsed) which is unsafe; update the logic where
parsed is used (the block returning parsed vs [...demoChecks], and the similar
block at lines ~316-318) to validate each item is an object and has string
fields "priority", "status", and "dueDate" (and any other required checklist
properties) before accepting it—e.g., filter or map parsed to include only items
that pass typeof checks and required key presence, and if validation fails for
the array, fall back to demoChecks or call resetDemoData(); reference the parsed
variable and the resetDemoData handling so malformed localStorage/demo payloads
never get assigned to checks.

Comment thread js/app.js
@@ -132,8 +136,7 @@ function handleAddCheck(event) {
const owner = ownerInput.value.trim() || "Unassigned";
const dueDate = dueDateInput.value || new Date().toISOString().slice(0, 10);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle date-only values in local time instead of UTC.

Line 137 uses toISOString(), and Lines 375-392 parse YYYY-MM-DD with new Date(...). In US time zones that shifts calendar dates, so due dates can render a day early, dueSoon can be off by one, and blank due dates can default to the wrong day around midnight.

Suggested fix
+function toLocalDateString(date = new Date()) {
+  const year = date.getFullYear()
+  const month = String(date.getMonth() + 1).padStart(2, "0")
+  const day = String(date.getDate()).padStart(2, "0")
+  return `${year}-${month}-${day}`
+}
+
+function parseLocalDate(dateValue) {
+  if (!dateValue) {
+    return null
+  }
+
+  const [year, month, day] = dateValue.split("-").map(Number)
+  return new Date(year, month - 1, day)
+}
+
 function handleAddCheck(event) {
@@
-  const dueDate = dueDateInput.value || new Date().toISOString().slice(0, 10);
+  const dueDate = dueDateInput.value || toLocalDateString();
@@
 function daysUntil(dateValue) {
-  const today = new Date();
-  const target = new Date(dateValue);
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+
+  const target = parseLocalDate(dateValue);
+  if (!target || Number.isNaN(target.getTime())) {
+    return Number.POSITIVE_INFINITY;
+  }
+
   const difference = target.getTime() - today.getTime();
   return Math.ceil(difference / 86400000);
 }
@@
 function formatDate(dateValue) {
   if (!dateValue) {
     return "No date";
   }
 
+  const parsed = parseLocalDate(dateValue);
+  if (!parsed || Number.isNaN(parsed.getTime())) {
+    return "Invalid date";
+  }
+
   return new Intl.DateTimeFormat("en", {
     month: "short",
     day: "numeric",
     year: "numeric",
-  }).format(new Date(dateValue));
+  }).format(parsed);
 }

Also applies to: 252-255, 375-392

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/app.js` at line 137, The code uses toISOString() and new
Date('YYYY-MM-DD') which treat/produce UTC dates and cause off-by-one local-date
errors; change places that create or parse date-only values (e.g., dueDate
default via dueDateInput.value / dueDate, the parser used in the block around
lines 375-392, and any dueSoon comparisons) to construct dates in local time by
splitting the YYYY-MM-DD string into year/month/day and using new Date(year,
month-1, day) (and when defaulting build today's YYYY-MM-DD from new Date()
local values), and ensure any comparisons normalize to local-midnight Date
objects so due dates and dueSoon logic use local calendar days instead of UTC.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
js/app.js (1)

298-301: 💤 Low value

Redundant updateMetrics() call.

applyFilters() at line 299 already calls renderApp(), which in turn calls updateMetrics(). The explicit updateMetrics() call at line 300 is redundant.

Suggested fix
   check.status = statusSelect.value;
   saveChecks();
   applyFilters();
-  updateMetrics();
   logActivity(`Changed "${check.title}" to ${check.status}.`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/app.js` around lines 298 - 301, The explicit call to updateMetrics() is
redundant because applyFilters() already triggers renderApp(), which calls
updateMetrics(); remove the standalone updateMetrics() invocation in the
sequence (the block containing saveChecks(), applyFilters(), updateMetrics(),
logActivity(...)) so the flow becomes saveChecks(); applyFilters();
logActivity(...), keeping renderApp()/updateMetrics() behavior intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@js/app.js`:
- Around line 298-301: The explicit call to updateMetrics() is redundant because
applyFilters() already triggers renderApp(), which calls updateMetrics(); remove
the standalone updateMetrics() invocation in the sequence (the block containing
saveChecks(), applyFilters(), updateMetrics(), logActivity(...)) so the flow
becomes saveChecks(); applyFilters(); logActivity(...), keeping
renderApp()/updateMetrics() behavior intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06afc52f-6202-42b3-b9bf-246a36e0e9a3

📥 Commits

Reviewing files that changed from the base of the PR and between 47c207c and 399090d.

📒 Files selected for processing (1)
  • js/app.js

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant