feat: add confirm_modal_dynamic macro for dynamic confirmation messages
Added a new macro that accepts an Alpine.js expression for the message, allowing runtime data to be included in confirmation dialogs. Updated admin-user-edit.html to use this macro for platform removal confirmation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
{% extends "admin/base.html" %}
|
{% extends "admin/base.html" %}
|
||||||
{% from 'shared/macros/alerts.html' import loading_state %}
|
{% from 'shared/macros/alerts.html' import loading_state %}
|
||||||
{% from 'shared/macros/headers.html' import edit_page_header %}
|
{% from 'shared/macros/headers.html' import edit_page_header %}
|
||||||
|
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
|
||||||
|
|
||||||
{% block title %}Edit Admin User{% endblock %}
|
{% block title %}Edit Admin User{% endblock %}
|
||||||
|
|
||||||
@@ -254,71 +255,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Remove Platform Confirmation Modal -->
|
<!-- Remove Platform Confirmation Modal -->
|
||||||
<div
|
{{ confirm_modal_dynamic(
|
||||||
x-show="showRemovePlatformModal"
|
'removePlatformModal',
|
||||||
x-cloak
|
'Remove Platform',
|
||||||
@keydown.escape.window="showRemovePlatformModal = false"
|
"'Are you sure you want to remove \"' + (platformToRemove?.name || '') + '\" from this admin?'",
|
||||||
class="fixed inset-0 z-50 overflow-y-auto"
|
'confirmRemovePlatform()',
|
||||||
role="dialog"
|
'showRemovePlatformModal',
|
||||||
aria-modal="true"
|
'Remove',
|
||||||
>
|
'Cancel',
|
||||||
<div
|
'warning'
|
||||||
x-show="showRemovePlatformModal"
|
) }}
|
||||||
x-transition:enter="ease-out duration-300"
|
|
||||||
x-transition:enter-start="opacity-0"
|
|
||||||
x-transition:enter-end="opacity-100"
|
|
||||||
x-transition:leave="ease-in duration-200"
|
|
||||||
x-transition:leave-start="opacity-100"
|
|
||||||
x-transition:leave-end="opacity-0"
|
|
||||||
class="fixed inset-0 bg-gray-500/50 dark:bg-gray-900/80 backdrop-blur-sm"
|
|
||||||
@click="showRemovePlatformModal = false"
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<div class="flex min-h-full items-center justify-center p-4">
|
|
||||||
<div
|
|
||||||
x-show="showRemovePlatformModal"
|
|
||||||
x-transition:enter="ease-out duration-300"
|
|
||||||
x-transition:enter-start="opacity-0 scale-95"
|
|
||||||
x-transition:enter-end="opacity-100 scale-100"
|
|
||||||
x-transition:leave="ease-in duration-200"
|
|
||||||
x-transition:leave-start="opacity-100 scale-100"
|
|
||||||
x-transition:leave-end="opacity-0 scale-95"
|
|
||||||
@click.stop
|
|
||||||
class="relative w-full max-w-md bg-white dark:bg-gray-800 rounded-xl shadow-xl p-6"
|
|
||||||
>
|
|
||||||
<div class="flex items-start">
|
|
||||||
<div class="flex-shrink-0 flex items-center justify-center w-12 h-12 rounded-full bg-yellow-100 dark:bg-yellow-900/30">
|
|
||||||
<span x-html="$icon('exclamation', 'w-6 h-6 text-yellow-600 dark:text-yellow-400')"></span>
|
|
||||||
</div>
|
|
||||||
<div class="ml-4 flex-1">
|
|
||||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">
|
|
||||||
Remove Platform
|
|
||||||
</h3>
|
|
||||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
Are you sure you want to remove <span class="font-semibold" x-text="platformToRemove?.name"></span> from this admin?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-6 flex justify-end gap-3">
|
|
||||||
<button
|
|
||||||
@click="showRemovePlatformModal = false"
|
|
||||||
type="button"
|
|
||||||
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click="confirmRemovePlatform(); showRemovePlatformModal = false"
|
|
||||||
type="button"
|
|
||||||
class="px-4 py-2 text-sm font-medium text-white rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 bg-yellow-600 hover:bg-yellow-700 focus:ring-yellow-500"
|
|
||||||
>
|
|
||||||
Remove
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_scripts %}
|
{% block extra_scripts %}
|
||||||
|
|||||||
@@ -300,6 +300,127 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
{#
|
||||||
|
Confirm Modal Dynamic
|
||||||
|
=====================
|
||||||
|
A confirmation dialog with dynamic message via Alpine.js variable.
|
||||||
|
Use this when the confirmation message needs to include runtime data.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- id: Unique modal ID
|
||||||
|
- title: Modal title (static string)
|
||||||
|
- message_var: Alpine.js variable containing the message (e.g., 'confirmMessage')
|
||||||
|
- confirm_action: Alpine.js action to execute on confirm
|
||||||
|
- show_var: Alpine.js variable controlling visibility
|
||||||
|
- confirm_text: Confirm button text (default: 'Confirm')
|
||||||
|
- cancel_text: Cancel button text (default: 'Cancel')
|
||||||
|
- variant: 'danger' | 'warning' | 'info' (default: 'danger')
|
||||||
|
- icon: Icon name (optional, auto-selected based on variant)
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
State: { showConfirm: false, itemToDelete: null }
|
||||||
|
Message var: `'Are you sure you want to delete "' + itemToDelete?.name + '"?'`
|
||||||
|
|
||||||
|
{{ confirm_modal_dynamic(
|
||||||
|
'deleteConfirm',
|
||||||
|
'Delete Item',
|
||||||
|
"'Are you sure you want to delete \"' + itemToDelete?.name + '\"?'",
|
||||||
|
'deleteItem()',
|
||||||
|
'showConfirm'
|
||||||
|
) }}
|
||||||
|
#}
|
||||||
|
{% macro confirm_modal_dynamic(id, title, message_var, confirm_action, show_var='isConfirmModalOpen', confirm_text='Confirm', cancel_text='Cancel', variant='danger', icon=none) %}
|
||||||
|
{% set variants = {
|
||||||
|
'danger': {
|
||||||
|
'icon_bg': 'bg-red-100 dark:bg-red-900/30',
|
||||||
|
'icon_color': 'text-red-600 dark:text-red-400',
|
||||||
|
'btn_class': 'bg-red-600 hover:bg-red-700 focus:ring-red-500'
|
||||||
|
},
|
||||||
|
'warning': {
|
||||||
|
'icon_bg': 'bg-yellow-100 dark:bg-yellow-900/30',
|
||||||
|
'icon_color': 'text-yellow-600 dark:text-yellow-400',
|
||||||
|
'btn_class': 'bg-yellow-600 hover:bg-yellow-700 focus:ring-yellow-500'
|
||||||
|
},
|
||||||
|
'info': {
|
||||||
|
'icon_bg': 'bg-blue-100 dark:bg-blue-900/30',
|
||||||
|
'icon_color': 'text-blue-600 dark:text-blue-400',
|
||||||
|
'btn_class': 'bg-blue-600 hover:bg-blue-700 focus:ring-blue-500'
|
||||||
|
}
|
||||||
|
} %}
|
||||||
|
{% set icons = {
|
||||||
|
'danger': 'exclamation-triangle',
|
||||||
|
'warning': 'exclamation',
|
||||||
|
'info': 'information-circle'
|
||||||
|
} %}
|
||||||
|
{% set modal_icon = icon if icon else icons[variant] %}
|
||||||
|
{% set style = variants[variant] %}
|
||||||
|
|
||||||
|
<div
|
||||||
|
x-show="{{ show_var }}"
|
||||||
|
x-cloak
|
||||||
|
@keydown.escape.window="{{ show_var }} = false"
|
||||||
|
class="fixed inset-0 z-50 overflow-y-auto"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
x-show="{{ show_var }}"
|
||||||
|
x-transition:enter="ease-out duration-300"
|
||||||
|
x-transition:enter-start="opacity-0"
|
||||||
|
x-transition:enter-end="opacity-100"
|
||||||
|
x-transition:leave="ease-in duration-200"
|
||||||
|
x-transition:leave-start="opacity-100"
|
||||||
|
x-transition:leave-end="opacity-0"
|
||||||
|
class="fixed inset-0 bg-gray-500/50 dark:bg-gray-900/80 backdrop-blur-sm"
|
||||||
|
@click="{{ show_var }} = false"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div class="flex min-h-full items-center justify-center p-4">
|
||||||
|
<div
|
||||||
|
x-show="{{ show_var }}"
|
||||||
|
x-transition:enter="ease-out duration-300"
|
||||||
|
x-transition:enter-start="opacity-0 scale-95"
|
||||||
|
x-transition:enter-end="opacity-100 scale-100"
|
||||||
|
x-transition:leave="ease-in duration-200"
|
||||||
|
x-transition:leave-start="opacity-100 scale-100"
|
||||||
|
x-transition:leave-end="opacity-0 scale-95"
|
||||||
|
@click.stop
|
||||||
|
class="relative w-full max-w-md bg-white dark:bg-gray-800 rounded-xl shadow-xl p-6"
|
||||||
|
>
|
||||||
|
<div class="flex items-start">
|
||||||
|
<div class="flex-shrink-0 flex items-center justify-center w-12 h-12 rounded-full {{ style.icon_bg }}">
|
||||||
|
<span x-html="$icon('{{ modal_icon }}', 'w-6 h-6 {{ style.icon_color }}')" class="{{ style.icon_color }}"></span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-4 flex-1">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400" x-text="{{ message_var }}"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 flex justify-end gap-3">
|
||||||
|
<button
|
||||||
|
@click="{{ show_var }} = false"
|
||||||
|
type="button"
|
||||||
|
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"
|
||||||
|
>
|
||||||
|
{{ cancel_text }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="{{ confirm_action }}; {{ show_var }} = false"
|
||||||
|
type="button"
|
||||||
|
class="px-4 py-2 text-sm font-medium text-white rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 {{ style.btn_class }}"
|
||||||
|
>
|
||||||
|
{{ confirm_text }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{#
|
{#
|
||||||
Form Modal
|
Form Modal
|
||||||
==========
|
==========
|
||||||
|
|||||||
Reference in New Issue
Block a user