diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd7eb89..f2c54e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Changed
+- **Unified confirm dialog — pure HTMX `hx-confirm` + `
+
Delete
+ hx-confirm="Delete this product? This cannot be undone.">Delete
{% endif %}
diff --git a/web/src/padelnomics/admin/templates/admin/affiliate_program_form.html b/web/src/padelnomics/admin/templates/admin/affiliate_program_form.html
index 9c2949d..3ec2a03 100644
--- a/web/src/padelnomics/admin/templates/admin/affiliate_program_form.html
+++ b/web/src/padelnomics/admin/templates/admin/affiliate_program_form.html
@@ -120,10 +120,10 @@ document.addEventListener('DOMContentLoaded', function() {
Cancel
{% if editing %}
-
+
Delete
+ hx-confirm="Delete this program? Blocked if products reference it.">Delete
{% endif %}
diff --git a/web/src/padelnomics/admin/templates/admin/articles.html b/web/src/padelnomics/admin/templates/admin/articles.html
index c8133d4..80acc2a 100644
--- a/web/src/padelnomics/admin/templates/admin/articles.html
+++ b/web/src/padelnomics/admin/templates/admin/articles.html
@@ -11,9 +11,10 @@
diff --git a/web/src/padelnomics/admin/templates/admin/audience_contacts.html b/web/src/padelnomics/admin/templates/admin/audience_contacts.html
index b48de55..e3e2187 100644
--- a/web/src/padelnomics/admin/templates/admin/audience_contacts.html
+++ b/web/src/padelnomics/admin/templates/admin/audience_contacts.html
@@ -27,10 +27,11 @@
{{ c.email if c.email is defined else (c.get('email', '-') if c is mapping else '-') }}
{{ (c.created_at if c.created_at is defined else (c.get('created_at', '-') if c is mapping else '-'))[:16] if c else '-' }}
-
+
- Remove
+ Remove
diff --git a/web/src/padelnomics/admin/templates/admin/base_admin.html b/web/src/padelnomics/admin/templates/admin/base_admin.html
index 28323f6..6f8c0aa 100644
--- a/web/src/padelnomics/admin/templates/admin/base_admin.html
+++ b/web/src/padelnomics/admin/templates/admin/base_admin.html
@@ -228,21 +228,29 @@
-
- Cancel
- Confirm
-
+
+ Cancel
+ Confirm
+
{% endblock %}
diff --git a/web/src/padelnomics/admin/templates/admin/generate_form.html b/web/src/padelnomics/admin/templates/admin/generate_form.html
index 1b7c137..33e3298 100644
--- a/web/src/padelnomics/admin/templates/admin/generate_form.html
+++ b/web/src/padelnomics/admin/templates/admin/generate_form.html
@@ -19,7 +19,7 @@
No data rows found. Run the data pipeline to populate {{ config_data.data_table }}.
{% else %}
-
+
@@ -45,7 +45,8 @@
-
+
Generate Articles
diff --git a/web/src/padelnomics/admin/templates/admin/partials/affiliate_program_results.html b/web/src/padelnomics/admin/templates/admin/partials/affiliate_program_results.html
index 5d79cce..3de0bbd 100644
--- a/web/src/padelnomics/admin/templates/admin/partials/affiliate_program_results.html
+++ b/web/src/padelnomics/admin/templates/admin/partials/affiliate_program_results.html
@@ -21,10 +21,10 @@
Edit
-
+
Delete
+ hx-confirm="Delete {{ prog.name }}? This is blocked if products reference it.">Delete
diff --git a/web/src/padelnomics/admin/templates/admin/partials/affiliate_row.html b/web/src/padelnomics/admin/templates/admin/partials/affiliate_row.html
index 5846ec7..494c45b 100644
--- a/web/src/padelnomics/admin/templates/admin/partials/affiliate_row.html
+++ b/web/src/padelnomics/admin/templates/admin/partials/affiliate_row.html
@@ -20,10 +20,10 @@
{{ product.click_count or 0 }}
Edit
-
+
Delete
+ hx-confirm="Delete {{ product.name }}?">Delete
diff --git a/web/src/padelnomics/admin/templates/admin/partials/pipeline_extractions.html b/web/src/padelnomics/admin/templates/admin/partials/pipeline_extractions.html
index de54678..40a6691 100644
--- a/web/src/padelnomics/admin/templates/admin/partials/pipeline_extractions.html
+++ b/web/src/padelnomics/admin/templates/admin/partials/pipeline_extractions.html
@@ -29,10 +29,10 @@
-
+
-
+
Run All Extractors
@@ -112,11 +112,11 @@
{% if run.status == 'running' %}
+ class="m-0" hx-boost="true">
-
+ hx-confirm="Mark run #{{ run.run_id }} as failed? Only do this if the process is definitely dead.">
Mark Failed
diff --git a/web/src/padelnomics/admin/templates/admin/partials/pipeline_overview.html b/web/src/padelnomics/admin/templates/admin/partials/pipeline_overview.html
index 43209c8..ce22a1d 100644
--- a/web/src/padelnomics/admin/templates/admin/partials/pipeline_overview.html
+++ b/web/src/padelnomics/admin/templates/admin/partials/pipeline_overview.html
@@ -40,7 +40,7 @@
hx-target="#pipeline-overview-content"
hx-swap="outerHTML"
hx-vals='{"extractor": "{{ wf.name }}", "csrf_token": "{{ csrf_token() }}"}'
- onclick="if (!confirm('Run {{ wf.name }} extractor?')) return false;">Run
+ hx-confirm="Run {{ wf.name }} extractor?">Run
{{ wf.schedule_label }}
{% if run %}
diff --git a/web/src/padelnomics/admin/templates/admin/partials/pipeline_transform.html b/web/src/padelnomics/admin/templates/admin/partials/pipeline_transform.html
index 5f16034..2e5b786 100644
--- a/web/src/padelnomics/admin/templates/admin/partials/pipeline_transform.html
+++ b/web/src/padelnomics/admin/templates/admin/partials/pipeline_transform.html
@@ -53,7 +53,7 @@
hx-target="#pipeline-transform-content"
hx-swap="outerHTML"
hx-vals='{"step": "transform", "csrf_token": "{{ csrf_token() }}"}'
- onclick="if (!confirm('Run SQLMesh transform (prod --auto-apply)?')) return false;">
+ hx-confirm="Run SQLMesh transform (prod --auto-apply)?">
Run Transform
@@ -107,7 +107,7 @@
hx-target="#pipeline-transform-content"
hx-swap="outerHTML"
hx-vals='{"step": "export", "csrf_token": "{{ csrf_token() }}"}'
- onclick="if (!confirm('Export serving tables (lakehouse → analytics.duckdb)?')) return false;">
+ hx-confirm="Export serving tables (lakehouse → analytics.duckdb)?">
Run Export
@@ -138,7 +138,7 @@
hx-target="#pipeline-transform-content"
hx-swap="outerHTML"
hx-vals='{"step": "pipeline", "csrf_token": "{{ csrf_token() }}"}'
- onclick="if (!confirm('Run full ELT pipeline (extract → transform → export)?')) return false;">
+ hx-confirm="Run full ELT pipeline (extract → transform → export)?">
Run Full Pipeline
diff --git a/web/src/padelnomics/admin/templates/admin/partials/scenario_results.html b/web/src/padelnomics/admin/templates/admin/partials/scenario_results.html
index d742914..3ba3047 100644
--- a/web/src/padelnomics/admin/templates/admin/partials/scenario_results.html
+++ b/web/src/padelnomics/admin/templates/admin/partials/scenario_results.html
@@ -36,9 +36,10 @@
PDF EN
PDF DE
Edit
-
+
- Delete
+ Delete
diff --git a/web/src/padelnomics/admin/templates/admin/pipeline.html b/web/src/padelnomics/admin/templates/admin/pipeline.html
index 7f8d216..dca3454 100644
--- a/web/src/padelnomics/admin/templates/admin/pipeline.html
+++ b/web/src/padelnomics/admin/templates/admin/pipeline.html
@@ -56,11 +56,11 @@
Extraction status, data catalog, and ad-hoc query editor
-
+
-
+
Run Pipeline
diff --git a/web/src/padelnomics/admin/templates/admin/template_detail.html b/web/src/padelnomics/admin/templates/admin/template_detail.html
index d0ca524..5395c02 100644
--- a/web/src/padelnomics/admin/templates/admin/template_detail.html
+++ b/web/src/padelnomics/admin/templates/admin/template_detail.html
@@ -13,9 +13,10 @@