Switch flash buttons from Alpine to vanilla JS event listeners
Alpine x-on:click on the flash bar was unreliable. Replace with
an inline script that attaches click handlers directly via
addEventListener. The undo path is stored in a data attribute
on the wrapper element.

https://claude.ai/code/session_01NLGBcJgbiyWGDBGFh8DRas
change
commit 97676ee89662fd54e91ca9c467ec44f45827e60c
author Claude <noreply@anthropic.com>
date
parent 55c1932f
diff --git a/lib/ketchup/views/dashboard.rb b/lib/ketchup/views/dashboard.rb
index edd98ff..3e5a106 100644
--- a/lib/ketchup/views/dashboard.rb
+++ b/lib/ketchup/views/dashboard.rb
@@ -42,24 +42,33 @@ module Views
 
       undo_path = @flash["undo_path"]
 
-      div(class: "flash-wrap") do
-        div(class: "flash-bar", "x-data": true) do
+      div(class: "flash-wrap", data: { undo_path: undo_path }.compact) do
+        div(class: "flash-bar") do
           span(class: "flash-message") { @flash["message"] }
-          if undo_path
-            button(
-              class: "flash-undo-btn",
-              "x-on:click": "fetch('#{undo_path}', {method: 'DELETE'}).then(() => location.reload())"
-            ) { "Undo" }
-          end
-          button(
-            class: "flash-close-btn",
-            "x-on:click": "$el.closest('.flash-wrap').remove()",
-            **{ "aria-label": "Dismiss" }
-          ) { "\u00d7" }
+          button(class: "flash-undo-btn", hidden: !undo_path) { "Undo" }
+          button(class: "flash-close-btn", **{ "aria-label": "Dismiss" }) { "\u00d7" }
         end
+        script { raw safe(flash_script) }
       end
     end
 
+    def flash_script
+      <<~JS
+        (function() {
+          var wrap = document.currentScript.parentElement;
+          var undo = wrap.querySelector('.flash-undo-btn');
+          var close = wrap.querySelector('.flash-close-btn');
+          var path = wrap.dataset.undoPath;
+          if (undo && path) {
+            undo.addEventListener('click', function() {
+              fetch(path, {method: 'DELETE'}).then(function() { location.reload(); });
+            });
+          }
+          close.addEventListener('click', function() { wrap.remove(); });
+        })();
+      JS
+    end
+
     def render_focus(overdue)
       if overdue.empty?
         section(class: "section section--focus") do