feat(admin): replace browser confirm() dialogs with native <dialog> modal

- Add styled <dialog id="confirm-dialog"> to base_admin.html — frosted
  backdrop, rounded card, Cancel / Confirm buttons
- Add confirmAction(message, form) JS helper — clones OK button to
  avoid listener accumulation, calls form.submit() on confirm
- Replace all 5 onclick="return confirm()" across templates with
  type="button" + confirmAction(..., this.closest('form'))
  · articles.html — Rebuild All
  · template_detail.html — Regenerate
  · generate_form.html — Generate Articles
  · scenario_results.html — Delete scenario
  · audience_contacts.html — Remove contact

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Deeman
2026-02-24 10:33:38 +01:00
parent 6bd92c69ce
commit bd79617851
6 changed files with 33 additions and 5 deletions

View File

@@ -18,7 +18,7 @@
<a href="{{ url_for('admin.article_new') }}" class="btn btn-sm">New Article</a> <a href="{{ url_for('admin.article_new') }}" class="btn btn-sm">New Article</a>
<form method="post" action="{{ url_for('admin.rebuild_all') }}" class="m-0" style="display:inline"> <form method="post" action="{{ url_for('admin.rebuild_all') }}" class="m-0" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn-outline btn-sm" onclick="return confirm('Rebuild all articles?')">Rebuild All</button> <button type="button" class="btn-outline btn-sm" onclick="confirmAction('Rebuild all articles? This will re-render every article from its template.', this.closest('form'))">Rebuild All</button>
</form> </form>
</div> </div>
</header> </header>

View File

@@ -30,7 +30,7 @@
<form method="post" action="{{ url_for('admin.audience_contact_remove', audience_id=audience.audience_id) }}" style="display:inline"> <form method="post" action="{{ url_for('admin.audience_contact_remove', audience_id=audience.audience_id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="contact_id" value="{{ c.id if c.id is defined else (c.get('id', '') if c is mapping else '') }}"> <input type="hidden" name="contact_id" value="{{ c.id if c.id is defined else (c.get('id', '') if c is mapping else '') }}">
<button type="submit" class="btn-outline btn-sm" style="color:#DC2626" onclick="return confirm('Remove this contact?')">Remove</button> <button type="button" class="btn-outline btn-sm" style="color:#DC2626" onclick="confirmAction('Remove this contact from the audience?', this.closest('form'))">Remove</button>
</form> </form>
</td> </td>
</tr> </tr>

View File

@@ -27,6 +27,14 @@
.admin-main { flex: 1; padding: 2rem; overflow-y: auto; } .admin-main { flex: 1; padding: 2rem; overflow-y: auto; }
#confirm-dialog {
border: none; border-radius: 12px; padding: 1.5rem; max-width: 380px; width: 90%;
box-shadow: 0 20px 60px rgba(0,0,0,0.15), 0 4px 16px rgba(0,0,0,0.08);
}
#confirm-dialog::backdrop { background: rgba(15,23,42,0.45); backdrop-filter: blur(3px); }
#confirm-dialog p { margin: 0 0 1.25rem; font-size: 0.9375rem; color: #0F172A; line-height: 1.55; }
#confirm-dialog .dialog-actions { display: flex; gap: 0.5rem; justify-content: flex-end; }
@media (max-width: 768px) { @media (max-width: 768px) {
.admin-layout { flex-direction: column; } .admin-layout { flex-direction: column; }
.admin-sidebar { .admin-sidebar {
@@ -130,4 +138,24 @@
{% block admin_content %}{% endblock %} {% block admin_content %}{% endblock %}
</main> </main>
</div> </div>
<dialog id="confirm-dialog">
<p id="confirm-msg"></p>
<div class="dialog-actions">
<button id="confirm-cancel" class="btn-outline btn-sm">Cancel</button>
<button id="confirm-ok" class="btn btn-sm">Confirm</button>
</div>
</dialog>
<script>
function confirmAction(message, form) {
var dialog = document.getElementById('confirm-dialog');
document.getElementById('confirm-msg').textContent = message;
var ok = document.getElementById('confirm-ok');
var newOk = ok.cloneNode(true);
ok.replaceWith(newOk);
newOk.addEventListener('click', function() { dialog.close(); form.submit(); });
document.getElementById('confirm-cancel').addEventListener('click', function() { dialog.close(); }, { once: true });
dialog.showModal();
}
</script>
{% endblock %} {% endblock %}

View File

@@ -45,7 +45,7 @@
</p> </p>
</div> </div>
<button type="submit" class="btn" style="width: 100%;" onclick="return confirm('Generate articles? Existing articles will be updated.')"> <button type="button" class="btn" style="width: 100%;" onclick="confirmAction('Generate articles? Existing articles will be updated in-place.', this.closest('form'))">
Generate Articles Generate Articles
</button> </button>
</form> </form>

View File

@@ -32,7 +32,7 @@
<a href="{{ url_for('admin.scenario_edit', scenario_id=s.id) }}" class="btn-outline btn-sm">Edit</a> <a href="{{ url_for('admin.scenario_edit', scenario_id=s.id) }}" class="btn-outline btn-sm">Edit</a>
<form method="post" action="{{ url_for('admin.scenario_delete', scenario_id=s.id) }}" class="m-0" style="display: inline;"> <form method="post" action="{{ url_for('admin.scenario_delete', scenario_id=s.id) }}" class="m-0" style="display: inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn-outline btn-sm" onclick="return confirm('Delete this scenario?')">Delete</button> <button type="button" class="btn-outline btn-sm" onclick="confirmAction('Delete this scenario? This cannot be undone.', this.closest('form'))">Delete</button>
</form> </form>
</td> </td>
</tr> </tr>

View File

@@ -15,7 +15,7 @@
<a href="{{ url_for('admin.template_generate', slug=config_data.slug) }}" class="btn">Generate Articles</a> <a href="{{ url_for('admin.template_generate', slug=config_data.slug) }}" class="btn">Generate Articles</a>
<form method="post" action="{{ url_for('admin.template_regenerate', slug=config_data.slug) }}" style="display:inline"> <form method="post" action="{{ url_for('admin.template_regenerate', slug=config_data.slug) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn-outline" onclick="return confirm('Regenerate all articles for this template with fresh data?')"> <button type="button" class="btn-outline" onclick="confirmAction('Regenerate all articles for this template with fresh data? Existing articles will be overwritten.', this.closest('form'))">
Regenerate Regenerate
</button> </button>
</form> </form>