fix: replace all native confirm() dialogs with styled modal macros
Some checks failed
CI / ruff (push) Successful in 9s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

Migrated ~68 native browser confirm() calls across 74 files to use the
project's confirm_modal/confirm_modal_dynamic Jinja2 macros, providing
consistent styled confirmation dialogs instead of plain browser popups.

Modules updated: core, tenancy, cms, marketplace, messaging, billing,
customers, orders, cart. Uses danger/warning/info variants and
double-confirm pattern for destructive delete operations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 16:56:25 +01:00
parent 182610283d
commit 167bb50f4f
74 changed files with 939 additions and 436 deletions

View File

@@ -42,6 +42,11 @@
loaded: false, loaded: false,
error: null, error: null,
// Limit reached modal state
showLimitReachedConfirm: false,
limitReachedMessage: '',
limitReachedUpgradeUrl: null,
// Computed-like getters // Computed-like getters
get hasLimitsApproaching() { get hasLimitsApproaching() {
return this.usage?.has_limits_approaching || false; return this.usage?.has_limits_approaching || false;
@@ -193,18 +198,24 @@
const limitName = limitNames[limitType] || limitType; const limitName = limitNames[limitType] || limitType;
const message = response.message || `You've reached your ${limitName} limit.`; const message = response.message || `You've reached your ${limitName} limit.`;
// Use browser confirm for simplicity - could be replaced with custom modal const details = `${message} Current usage: ${response.current}/${response.limit}. ` +
const shouldUpgrade = confirm(
`${message}\n\n` +
`Current: ${response.current}/${response.limit}\n\n` +
(response.upgrade_tier_name (response.upgrade_tier_name
? `Upgrade to ${response.upgrade_tier_name} to get more ${limitName}.\n\nGo to billing page?` ? `Upgrade to ${response.upgrade_tier_name} to get more ${limitName}.`
: 'Contact support for more capacity.') : 'Contact support for more capacity.');
);
if (shouldUpgrade && response.upgrade_tier_code) { this.limitReachedMessage = details;
window.location.href = this.getBillingUrl(); this.limitReachedUpgradeUrl = response.upgrade_tier_code ? this.getBillingUrl() : null;
this.showLimitReachedConfirm = true;
},
/**
* Handle upgrade confirmation from limit reached modal
*/
confirmUpgrade() {
if (this.limitReachedUpgradeUrl) {
window.location.href = this.limitReachedUpgradeUrl;
} }
this.showLimitReachedConfirm = false;
}, },
/** /**

View File

@@ -22,6 +22,8 @@ function storeBilling() {
showTiersModal: false, showTiersModal: false,
showAddonsModal: false, showAddonsModal: false,
showCancelModal: false, showCancelModal: false,
showCancelAddonConfirm: false,
pendingCancelAddon: null,
showSuccessMessage: false, showSuccessMessage: false,
showCancelMessage: false, showCancelMessage: false,
showAddonSuccessMessage: false, showAddonSuccessMessage: false,
@@ -172,10 +174,6 @@ function storeBilling() {
}, },
async cancelAddon(addon) { async cancelAddon(addon) {
if (!confirm(`Are you sure you want to cancel ${addon.addon_name}?`)) {
return;
}
try { try {
await apiClient.delete(`/store/billing/addons/${addon.id}`); await apiClient.delete(`/store/billing/addons/${addon.id}`);
Utils.showToast(I18n.t('billing.messages.addon_cancelled_successfully'), 'success'); Utils.showToast(I18n.t('billing.messages.addon_cancelled_successfully'), 'success');

View File

@@ -68,6 +68,11 @@ function storeInvoices() {
status: '' status: ''
}, },
// Update status confirm state
showUpdateStatusConfirm: false,
pendingStatusInvoice: null,
pendingNewStatus: null,
// Create invoice modal // Create invoice modal
showCreateModal: false, showCreateModal: false,
createForm: { createForm: {
@@ -291,16 +296,6 @@ function storeInvoices() {
* Update invoice status * Update invoice status
*/ */
async updateStatus(invoice, newStatus) { async updateStatus(invoice, newStatus) {
const statusLabels = {
'issued': 'mark as issued',
'paid': 'mark as paid',
'cancelled': 'cancel'
};
if (!confirm(`Are you sure you want to ${statusLabels[newStatus] || newStatus} this invoice?`)) {
return;
}
try { try {
await apiClient.put(`/store/invoices/${invoice.id}/status`, { await apiClient.put(`/store/invoices/${invoice.id}/status`, {
status: newStatus status: newStatus

View File

@@ -1,5 +1,6 @@
{# app/modules/billing/templates/billing/merchant/subscription-detail.html #} {# app/modules/billing/templates/billing/merchant/subscription-detail.html #}
{% extends "merchant/base.html" %} {% extends "merchant/base.html" %}
{% from 'shared/macros/modals.html' import confirm_modal %}
{% block title %}Subscription Details{% endblock %} {% block title %}Subscription Details{% endblock %}
@@ -119,7 +120,7 @@
<span class="inline-block mt-3 px-3 py-1 text-xs font-semibold text-purple-700 dark:text-purple-400 bg-purple-100 dark:bg-purple-900/30 rounded-full">Current Plan</span> <span class="inline-block mt-3 px-3 py-1 text-xs font-semibold text-purple-700 dark:text-purple-400 bg-purple-100 dark:bg-purple-900/30 rounded-full">Current Plan</span>
</template> </template>
<template x-if="!t.is_current"> <template x-if="!t.is_current">
<button @click="changeTier(t.code)" <button @click="pendingTierCode = t.code; showChangeTierConfirm = true"
:disabled="changingTier" :disabled="changingTier"
class="mt-3 inline-flex items-center px-4 py-2 text-sm font-medium text-white rounded-lg transition-colors disabled:opacity-50" class="mt-3 inline-flex items-center px-4 py-2 text-sm font-medium text-white rounded-lg transition-colors disabled:opacity-50"
:class="t.can_upgrade ? 'bg-purple-600 hover:bg-purple-700' : 'bg-gray-600 hover:bg-gray-700'" :class="t.can_upgrade ? 'bg-purple-600 hover:bg-purple-700' : 'bg-gray-600 hover:bg-gray-700'"
@@ -133,6 +134,9 @@
</div> </div>
</div> </div>
<!-- Change Tier Confirm Modal -->
{{ confirm_modal('changeTierConfirm', 'Change Plan', 'Are you sure you want to change your plan to this tier? Your billing will be adjusted accordingly.', 'changeTier(pendingTierCode)', 'showChangeTierConfirm', 'Change Plan', 'Cancel', 'warning') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}
@@ -145,6 +149,8 @@ function merchantSubscriptionDetail() {
subscription: null, subscription: null,
availableTiers: [], availableTiers: [],
changingTier: false, changingTier: false,
showChangeTierConfirm: false,
pendingTierCode: null,
init() { init() {
this.loadSubscription(); this.loadSubscription();
@@ -182,8 +188,6 @@ function merchantSubscriptionDetail() {
}, },
async changeTier(tierCode) { async changeTier(tierCode) {
if (!confirm(`Are you sure you want to change your plan to this tier?`)) return;
this.changingTier = true; this.changingTier = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;

View File

@@ -1,7 +1,7 @@
{# app/templates/store/billing.html #} {# app/templates/store/billing.html #}
{% extends "store/base.html" %} {% extends "store/base.html" %}
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/modals.html' import modal_simple %} {% from 'shared/macros/modals.html' import modal_simple, confirm_modal_dynamic %}
{% block title %}Billing & Subscription{% endblock %} {% block title %}Billing & Subscription{% endblock %}
@@ -308,7 +308,7 @@
<span x-text="addon.period_end ? `Renews ${formatDate(addon.period_end)}` : 'Active'"></span> <span x-text="addon.period_end ? `Renews ${formatDate(addon.period_end)}` : 'Active'"></span>
</p> </p>
</div> </div>
<button @click="cancelAddon(addon)" <button @click="pendingCancelAddon = addon; showCancelAddonConfirm = true"
class="px-3 py-1 text-sm font-medium text-red-600 bg-red-100 rounded-lg hover:bg-red-200 dark:bg-red-900/50 dark:text-red-400"> class="px-3 py-1 text-sm font-medium text-red-600 bg-red-100 rounded-lg hover:bg-red-200 dark:bg-red-900/50 dark:text-red-400">
Cancel Cancel
</button> </button>
@@ -355,6 +355,9 @@
</div> </div>
</div> </div>
<!-- Cancel Add-on Confirm Modal -->
{{ confirm_modal_dynamic('cancelAddonConfirm', 'Cancel Add-on', "'Are you sure you want to cancel ' + (pendingCancelAddon?.addon_name || 'this add-on') + '?'", 'cancelAddon(pendingCancelAddon)', 'showCancelAddonConfirm', 'Cancel Add-on', 'Keep', 'danger') }}
<!-- Cancel Subscription Modal --> <!-- Cancel Subscription Modal -->
<div x-show="showCancelModal" <div x-show="showCancelModal"
x-transition:enter="transition ease-out duration-150" x-transition:enter="transition ease-out duration-150"

View File

@@ -1,5 +1,6 @@
{# app/templates/storefront/cart.html #} {# app/templates/storefront/cart.html #}
{% extends "storefront/base.html" %} {% extends "storefront/base.html" %}
{% from 'shared/macros/modals.html' import confirm_modal %}
{% block title %}Shopping Cart{% endblock %} {% block title %}Shopping Cart{% endblock %}
@@ -107,7 +108,7 @@
</div> </div>
<button <button
@click="removeItem(item.product_id)" @click="pendingRemoveProductId = item.product_id; showRemoveItemConfirm = true"
:disabled="updating" :disabled="updating"
class="w-10 h-10 flex items-center justify-center border border-gray-300 dark:border-gray-600 rounded hover:bg-red-600 hover:border-red-600 hover:text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="w-10 h-10 flex items-center justify-center border border-gray-300 dark:border-gray-600 rounded hover:bg-red-600 hover:border-red-600 hover:text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Remove from cart" title="Remove from cart"
@@ -165,6 +166,9 @@
</div> </div>
</div> </div>
<!-- Remove Cart Item Confirm Modal -->
{{ confirm_modal('removeItemConfirm', 'Remove Item', 'Remove this item from your cart?', 'removeItem(pendingRemoveProductId)', 'showRemoveItemConfirm', 'Remove', 'Cancel', 'danger') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}
@@ -179,6 +183,8 @@ document.addEventListener('alpine:init', () => {
items: [], items: [],
loading: false, loading: false,
updating: false, updating: false,
showRemoveItemConfirm: false,
pendingRemoveProductId: null,
// Computed properties // Computed properties
get totalItems() { get totalItems() {
@@ -271,10 +277,6 @@ document.addEventListener('alpine:init', () => {
// Remove item from cart // Remove item from cart
async removeItem(productId) { async removeItem(productId) {
if (!confirm('Remove this item from your cart?')) {
return;
}
this.updating = true; this.updating = true;
try { try {

View File

@@ -21,6 +21,12 @@ function contentPagesManager() {
loading: false, loading: false,
error: null, error: null,
// Modal state
showCreateHomepageConfirm: false,
pendingHomepagePlatform: null,
showDeletePageConfirm: false,
pageToDelete: null,
// Tabs and filters // Tabs and filters
activeTab: 'all', // all, platform_marketing, store_defaults, store_overrides activeTab: 'all', // all, platform_marketing, store_defaults, store_overrides
searchQuery: '', searchQuery: '',
@@ -188,13 +194,19 @@ function contentPagesManager() {
// Show a toast and offer to create // Show a toast and offer to create
if (slug === 'home') { if (slug === 'home') {
// Offer to create homepage // Offer to create homepage
if (confirm(`No homepage found for ${platformCode}. Would you like to create one?`)) { this.pendingHomepagePlatform = platformCode;
window.location.href = `/admin/content-pages/create?platform_code=${platformCode}&slug=home&is_platform_page=true`; this.showCreateHomepageConfirm = true;
}
} }
} }
}, },
// Create homepage for a platform (called from confirm modal)
createHomepage() {
if (this.pendingHomepagePlatform) {
window.location.href = `/admin/content-pages/create?platform_code=${this.pendingHomepagePlatform}&slug=home&is_platform_page=true`;
}
},
// Get page tier label (three-tier system) // Get page tier label (three-tier system)
getPageTierLabel(page) { getPageTierLabel(page) {
if (page.store_id) { if (page.store_id) {
@@ -220,12 +232,14 @@ function contentPagesManager() {
} }
}, },
// Prompt delete page confirmation
promptDeletePage(page) {
this.pageToDelete = page;
this.showDeletePageConfirm = true;
},
// Delete a page // Delete a page
async deletePage(page) { async deletePage(page) {
if (!confirm(`Are you sure you want to delete "${page.title}"?`)) {
return;
}
try { try {
contentPagesLog.info(`Deleting page: ${page.id}`); contentPagesLog.info(`Deleting page: ${page.id}`);

View File

@@ -40,6 +40,9 @@ function storeContentPageEditor(pageId) {
loadingDefault: false, loadingDefault: false,
defaultContent: null, defaultContent: null,
// Delete confirmation modal state
showDeletePageConfirm: false,
// Initialize // Initialize
async init() { async init() {
// Prevent multiple initializations // Prevent multiple initializations
@@ -211,14 +214,6 @@ function storeContentPageEditor(pageId) {
// Delete page (revert to default for overrides) // Delete page (revert to default for overrides)
async deletePage() { async deletePage() {
const message = this.isOverride
? 'Are you sure you want to revert to the platform default? Your customizations will be lost.'
: 'Are you sure you want to delete this page? This cannot be undone.';
if (!confirm(message)) {
return;
}
try { try {
contentPageEditLog.info('Deleting page:', this.pageId); contentPageEditLog.info('Deleting page:', this.pageId);

View File

@@ -20,6 +20,10 @@ function storeContentPagesManager() {
activeTab: 'platform', activeTab: 'platform',
searchQuery: '', searchQuery: '',
// Modal state
showDeletePageConfirm: false,
pageToDelete: null,
// Data // Data
platformPages: [], // Platform default pages platformPages: [], // Platform default pages
customPages: [], // Store's own pages (overrides + custom) customPages: [], // Store's own pages (overrides + custom)
@@ -152,16 +156,14 @@ function storeContentPagesManager() {
} }
}, },
// Prompt delete page confirmation
promptDeletePage(page) {
this.pageToDelete = page;
this.showDeletePageConfirm = true;
},
// Delete a page // Delete a page
async deletePage(page) { async deletePage(page) {
const message = page.is_store_override
? `Are you sure you want to delete your override for "${page.title}"? The platform default will be shown instead.`
: `Are you sure you want to delete "${page.title}"? This cannot be undone.`;
if (!confirm(message)) {
return;
}
try { try {
contentPagesLog.info('Deleting page:', page.id); contentPagesLog.info('Deleting page:', page.id);

View File

@@ -105,6 +105,7 @@ function storeMedia() {
// Modal states // Modal states
showUploadModal: false, showUploadModal: false,
showDetailModal: false, showDetailModal: false,
showDeleteMediaConfirm: false,
selectedMedia: null, selectedMedia: null,
editingMedia: { editingMedia: {
filename: '', filename: '',
@@ -253,10 +254,6 @@ function storeMedia() {
async deleteMedia() { async deleteMedia() {
if (!this.selectedMedia) return; if (!this.selectedMedia) return;
if (!confirm(I18n.t('cms.confirmations.delete_file'))) {
return;
}
this.saving = true; this.saving = true;
try { try {

View File

@@ -3,6 +3,7 @@
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/tabs.html' import tabs_inline, tab_button %} {% from 'shared/macros/tabs.html' import tabs_inline, tab_button %}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
{% block title %}Content Pages{% endblock %} {% block title %}Content Pages{% endblock %}
@@ -142,7 +143,7 @@
<span x-html="$icon('edit', 'w-5 h-5')"></span> <span x-html="$icon('edit', 'w-5 h-5')"></span>
</a> </a>
<button <button
@click="deletePage(page)" @click="promptDeletePage(page)"
class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors" class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors"
title="Delete" title="Delete"
> >
@@ -175,6 +176,28 @@
Create First Page Create First Page
</a> </a>
</div> </div>
{{ confirm_modal_dynamic(
'createHomepageModal',
'Create Homepage',
"'No homepage found for ' + (pendingHomepagePlatform || '') + '. Would you like to create one?'",
'createHomepage()',
'showCreateHomepageConfirm',
'Create',
'Cancel',
'info'
) }}
{{ confirm_modal_dynamic(
'deletePageModal',
'Delete Page',
"'Are you sure you want to delete \"' + (pageToDelete?.title || '') + '\"?'",
'deletePage(pageToDelete)',
'showDeletePageConfirm',
'Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -3,7 +3,7 @@
{% from 'shared/macros/alerts.html' import loading_state, error_state, alert_dynamic %} {% from 'shared/macros/alerts.html' import loading_state, error_state, alert_dynamic %}
{% from 'shared/macros/headers.html' import back_button %} {% from 'shared/macros/headers.html' import back_button %}
{% from 'shared/macros/inputs.html' import number_stepper %} {% from 'shared/macros/inputs.html' import number_stepper %}
{% from 'shared/macros/modals.html' import modal %} {% from 'shared/macros/modals.html' import modal, confirm_modal_dynamic %}
{% block title %}{% if page_id %}Edit{% else %}Create{% endif %} Content Page{% endblock %} {% block title %}{% if page_id %}Edit{% else %}Create{% endif %} Content Page{% endblock %}
@@ -61,7 +61,7 @@
View Default View Default
</button> </button>
<button <button
@click="deletePage()" @click="showDeletePageConfirm = true"
class="inline-flex items-center px-3 py-1.5 text-xs font-medium text-red-700 bg-white dark:bg-red-900/50 dark:text-red-300 border border-red-300 dark:border-red-700 rounded-lg hover:bg-red-50 dark:hover:bg-red-900 transition-colors" class="inline-flex items-center px-3 py-1.5 text-xs font-medium text-red-700 bg-white dark:bg-red-900/50 dark:text-red-300 border border-red-300 dark:border-red-700 rounded-lg hover:bg-red-50 dark:hover:bg-red-900 transition-colors"
> >
<span x-html="$icon('arrow-uturn-left', 'w-4 h-4 mr-1')"></span> <span x-html="$icon('arrow-uturn-left', 'w-4 h-4 mr-1')"></span>
@@ -319,6 +319,17 @@
</div> </div>
</form> </form>
</div> </div>
{{ confirm_modal_dynamic(
'deletePageModal',
'Delete Page',
"isOverride ? 'Are you sure you want to revert to the platform default? Your customizations will be lost.' : 'Are you sure you want to delete this page? This cannot be undone.'",
'deletePage()',
'showDeletePageConfirm',
'Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -3,6 +3,7 @@
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/tabs.html' import tabs_inline, tab_button %} {% from 'shared/macros/tabs.html' import tabs_inline, tab_button %}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
{% block title %}Content Pages{% endblock %} {% block title %}Content Pages{% endblock %}
@@ -286,7 +287,7 @@
<span x-html="$icon('eye', 'w-5 h-5')"></span> <span x-html="$icon('eye', 'w-5 h-5')"></span>
</a> </a>
<button <button
@click="deletePage(page)" @click="promptDeletePage(page)"
class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors" class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors"
title="Delete" title="Delete"
> >
@@ -320,6 +321,17 @@
</a> </a>
</div> </div>
</div> </div>
{{ confirm_modal_dynamic(
'deletePageModal',
'Delete Page',
"pageToDelete?.is_store_override ? 'Are you sure you want to delete your override for \"' + (pageToDelete?.title || '') + '\"? The platform default will be shown instead.' : 'Are you sure you want to delete \"' + (pageToDelete?.title || '') + '\"? This cannot be undone.'",
'deletePage(pageToDelete)',
'showDeletePageConfirm',
'Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -3,7 +3,7 @@
{% from 'shared/macros/pagination.html' import pagination %} {% from 'shared/macros/pagination.html' import pagination %}
{% from 'shared/macros/headers.html' import page_header_flex, refresh_button %} {% from 'shared/macros/headers.html' import page_header_flex, refresh_button %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/modals.html' import modal_simple %} {% from 'shared/macros/modals.html' import modal_simple, confirm_modal %}
{% block title %}Media Library{% endblock %} {% block title %}Media Library{% endblock %}
@@ -412,7 +412,7 @@
<!-- Modal Footer --> <!-- Modal Footer -->
<div class="flex justify-between px-6 py-4 border-t dark:border-gray-700"> <div class="flex justify-between px-6 py-4 border-t dark:border-gray-700">
<button <button
@click="deleteMedia()" @click="showDeleteMediaConfirm = true"
class="px-4 py-2 text-sm font-medium text-red-600 border border-red-600 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20" class="px-4 py-2 text-sm font-medium text-red-600 border border-red-600 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20"
:disabled="saving" :disabled="saving"
> >
@@ -438,6 +438,17 @@
</div> </div>
</div> </div>
</div> </div>
{{ confirm_modal(
'deleteMediaModal',
'Delete File',
'Are you sure you want to delete this file? This action cannot be undone.',
'deleteMedia()',
'showDeleteMediaConfirm',
'Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -28,6 +28,8 @@ function adminMyMenuConfig() {
// Data // Data
menuConfig: null, menuConfig: null,
showShowAllModal: false,
showHideAllModal: false,
// Computed grouped items // Computed grouped items
get groupedItems() { get groupedItems() {
@@ -143,10 +145,6 @@ function adminMyMenuConfig() {
}, },
async showAll() { async showAll() {
if (!confirm(I18n.t('core.confirmations.show_all_menu_items'))) {
return;
}
this.saving = true; this.saving = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;
@@ -165,10 +163,6 @@ function adminMyMenuConfig() {
}, },
async resetToDefaults() { async resetToDefaults() {
if (!confirm(I18n.t('core.confirmations.hide_all_menu_items'))) {
return;
}
this.saving = true; this.saving = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;

View File

@@ -80,6 +80,8 @@ function adminSettings() {
sendingTestEmail: false, sendingTestEmail: false,
testEmailError: null, testEmailError: null,
testEmailSuccess: null, testEmailSuccess: null,
showResetEmailModal: false,
showCleanupLogsModal: false,
async init() { async init() {
// Load i18n translations // Load i18n translations
@@ -194,10 +196,6 @@ function adminSettings() {
}, },
async cleanupOldLogs() { async cleanupOldLogs() {
if (!confirm(`This will delete all logs older than ${this.logSettings.db_log_retention_days} days. Continue?`)) {
return;
}
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;
@@ -439,10 +437,6 @@ function adminSettings() {
}, },
async resetEmailSettings() { async resetEmailSettings() {
if (!confirm(I18n.t('core.confirmations.reset_email_settings'))) {
return;
}
this.saving = true; this.saving = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import alert_dynamic, error_state %} {% from 'shared/macros/alerts.html' import alert_dynamic, error_state %}
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/modals.html' import confirm_modal %}
{% block title %}My Menu{% endblock %} {% block title %}My Menu{% endblock %}
@@ -68,7 +69,7 @@
</p> </p>
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
@click="showAll()" @click="showShowAllModal = true"
:disabled="saving" :disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600"
> >
@@ -76,7 +77,7 @@
Show All Show All
</button> </button>
<button <button
@click="resetToDefaults()" @click="showHideAllModal = true"
:disabled="saving" :disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600"
> >
@@ -167,6 +168,31 @@
</div> </div>
</div> </div>
<!-- Confirmation Modals -->
{{ confirm_modal(
'showAllModal',
'Show All Menu Items',
'This will make all menu items visible in your sidebar. Are you sure?',
'showAll()',
'showShowAllModal',
'Show All',
'Cancel',
'info',
'eye'
) }}
{{ confirm_modal(
'hideAllModal',
'Hide All Menu Items',
'This will hide all non-mandatory menu items from your sidebar. Are you sure?',
'resetToDefaults()',
'showHideAllModal',
'Hide All',
'Cancel',
'warning',
'eye-off'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -4,6 +4,7 @@
{% from 'shared/macros/headers.html' import page_header_refresh %} {% from 'shared/macros/headers.html' import page_header_refresh %}
{% from 'shared/macros/tabs.html' import tabs_nav, tab_button %} {% from 'shared/macros/tabs.html' import tabs_nav, tab_button %}
{% from 'shared/macros/inputs.html' import number_stepper %} {% from 'shared/macros/inputs.html' import number_stepper %}
{% from 'shared/macros/modals.html' import confirm_modal, confirm_modal_dynamic %}
{% block title %}Platform Settings{% endblock %} {% block title %}Platform Settings{% endblock %}
@@ -208,7 +209,7 @@
View Logs View Logs
</a> </a>
<button <button
@click="cleanupOldLogs()" @click="showCleanupLogsModal = true"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 transition-colors duration-150 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 focus:outline-none focus:shadow-outline-gray" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 transition-colors duration-150 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 focus:outline-none focus:shadow-outline-gray"
> >
<span x-html="$icon('delete', 'w-4 h-4 mr-2')"></span> <span x-html="$icon('delete', 'w-4 h-4 mr-2')"></span>
@@ -355,7 +356,7 @@
<template x-if="emailSettings.has_db_overrides"> <template x-if="emailSettings.has_db_overrides">
<div class="mt-4"> <div class="mt-4">
<button <button
@click="resetEmailSettings()" @click="showResetEmailModal = true"
:disabled="saving" :disabled="saving"
class="px-4 py-2 text-sm font-medium text-red-600 dark:text-red-400 bg-white dark:bg-gray-700 border border-red-300 dark:border-red-600 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 disabled:opacity-50" class="px-4 py-2 text-sm font-medium text-red-600 dark:text-red-400 bg-white dark:bg-gray-700 border border-red-300 dark:border-red-600 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 disabled:opacity-50"
> >
@@ -775,6 +776,30 @@
</div> </div>
</div> </div>
<!-- Confirmation Modals -->
{{ confirm_modal(
'resetEmailModal',
'Reset Email Settings',
'This will remove all database overrides and revert email configuration to .env defaults. Are you sure?',
'resetEmailSettings()',
'showResetEmailModal',
'Reset to Defaults',
'Cancel',
'warning',
'refresh'
) }}
{{ confirm_modal_dynamic(
'cleanupLogsModal',
'Cleanup Old Logs',
"'This will permanently delete all logs older than ' + logSettings.db_log_retention_days + ' days. This action cannot be undone.'",
'cleanupOldLogs()',
'showCleanupLogsModal',
'Delete Old Logs',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -52,6 +52,10 @@ function adminCustomers() {
// Selected store (for prominent display and filtering) // Selected store (for prominent display and filtering)
selectedStore: null, selectedStore: null,
// Toggle status confirm state
showToggleStatusConfirm: false,
pendingToggleCustomer: null,
// Tom Select instance // Tom Select instance
storeSelectInstance: null, storeSelectInstance: null,
@@ -351,11 +355,6 @@ function adminCustomers() {
* Toggle customer active status * Toggle customer active status
*/ */
async toggleStatus(customer) { async toggleStatus(customer) {
const action = customer.is_active ? 'deactivate' : 'activate';
if (!confirm(`Are you sure you want to ${action} this customer?`)) {
return;
}
try { try {
const response = await apiClient.patch(`/admin/customers/${customer.id}/toggle-status`); const response = await apiClient.patch(`/admin/customers/${customer.id}/toggle-status`);
customer.is_active = response.is_active; customer.is_active = response.is_active;

View File

@@ -4,6 +4,7 @@
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/tables.html' import table_wrapper, table_header %} {% from 'shared/macros/tables.html' import table_wrapper, table_header %}
{% from 'shared/macros/pagination.html' import pagination %} {% from 'shared/macros/pagination.html' import pagination %}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
{% block title %}Customers{% endblock %} {% block title %}Customers{% endblock %}
@@ -278,7 +279,7 @@
<td class="px-4 py-3"> <td class="px-4 py-3">
<div class="flex items-center space-x-2 text-sm"> <div class="flex items-center space-x-2 text-sm">
<button <button
@click="toggleStatus(customer)" @click="pendingToggleCustomer = customer; showToggleStatusConfirm = true"
class="px-2 py-1 text-xs font-medium leading-5 rounded-lg" class="px-2 py-1 text-xs font-medium leading-5 rounded-lg"
:class="customer.is_active :class="customer.is_active
? 'text-red-600 hover:bg-red-100 dark:text-red-400 dark:hover:bg-red-700/50' ? 'text-red-600 hover:bg-red-100 dark:text-red-400 dark:hover:bg-red-700/50'
@@ -298,6 +299,9 @@
<!-- Pagination --> <!-- Pagination -->
{{ pagination() }} {{ pagination() }}
</div> </div>
<!-- Toggle Customer Status Confirm Modal -->
{{ confirm_modal_dynamic('toggleStatusConfirm', 'Change Customer Status', "'Are you sure you want to ' + (pendingToggleCustomer?.is_active ? 'deactivate' : 'activate') + ' this customer?'", 'toggleStatus(pendingToggleCustomer)', 'showToggleStatusConfirm', 'Confirm', 'Cancel', 'warning') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -48,6 +48,9 @@ function adminLetzshop() {
pendingOrders: 0 pendingOrders: 0
}, },
// Confirm modals
showDeleteStoreConfigModal: false,
// Configuration modal // Configuration modal
showConfigModal: false, showConfigModal: false,
selectedStore: null, selectedStore: null,
@@ -191,10 +194,6 @@ function adminLetzshop() {
* Delete store configuration * Delete store configuration
*/ */
async deleteStoreConfig() { async deleteStoreConfig() {
if (!confirm(I18n.t('marketplace.confirmations.remove_letzshop_config_store'))) {
return;
}
try { try {
await apiClient.delete(`/admin/letzshop/stores/${this.selectedStore.store_id}/credentials`); await apiClient.delete(`/admin/letzshop/stores/${this.selectedStore.store_id}/credentials`);
this.showConfigModal = false; this.showConfigModal = false;

View File

@@ -124,6 +124,15 @@ function adminMarketplaceLetzshop() {
productStats: { total: 0, active: 0, inactive: 0, last_sync: null }, productStats: { total: 0, active: 0, inactive: 0, last_sync: null },
showImportModal: false, showImportModal: false,
// Confirm modals
showDeclineOrderModal: false,
orderToDecline: null,
showConfirmAllItemsModal: false,
showDeclineAllItemsModal: false,
showDeleteCredentialsModal: false,
showIgnoreExceptionModal: false,
exceptionToIgnore: null,
// Modals // Modals
showTrackingModal: false, showTrackingModal: false,
showOrderModal: false, showOrderModal: false,
@@ -1010,8 +1019,6 @@ function adminMarketplaceLetzshop() {
async declineOrder(order) { async declineOrder(order) {
if (!this.selectedStore) return; if (!this.selectedStore) return;
if (!confirm(I18n.t('marketplace.confirmations.decline_order'))) return;
try { try {
await apiClient.post(`/admin/letzshop/stores/${this.selectedStore.id}/orders/${order.id}/reject`); await apiClient.post(`/admin/letzshop/stores/${this.selectedStore.id}/orders/${order.id}/reject`);
this.successMessage = 'Order declined'; this.successMessage = 'Order declined';
@@ -1128,8 +1135,6 @@ function adminMarketplaceLetzshop() {
async confirmAllItems(order) { async confirmAllItems(order) {
if (!this.selectedStore) return; if (!this.selectedStore) return;
if (!confirm(I18n.t('marketplace.confirmations.confirm_all_items'))) return;
try { try {
await apiClient.post( await apiClient.post(
`/admin/letzshop/stores/${this.selectedStore.id}/orders/${order.id}/confirm` `/admin/letzshop/stores/${this.selectedStore.id}/orders/${order.id}/confirm`
@@ -1149,8 +1154,6 @@ function adminMarketplaceLetzshop() {
async declineAllItems(order) { async declineAllItems(order) {
if (!this.selectedStore) return; if (!this.selectedStore) return;
if (!confirm(I18n.t('marketplace.confirmations.decline_all_items'))) return;
try { try {
await apiClient.post( await apiClient.post(
`/admin/letzshop/stores/${this.selectedStore.id}/orders/${order.id}/reject` `/admin/letzshop/stores/${this.selectedStore.id}/orders/${order.id}/reject`
@@ -1241,10 +1244,6 @@ function adminMarketplaceLetzshop() {
async deleteCredentials() { async deleteCredentials() {
if (!this.selectedStore) return; if (!this.selectedStore) return;
if (!confirm(I18n.t('marketplace.confirmations.remove_letzshop_config'))) {
return;
}
try { try {
await apiClient.delete(`/admin/letzshop/stores/${this.selectedStore.id}/credentials`); await apiClient.delete(`/admin/letzshop/stores/${this.selectedStore.id}/credentials`);
this.successMessage = 'Credentials removed'; this.successMessage = 'Credentials removed';
@@ -1459,10 +1458,6 @@ function adminMarketplaceLetzshop() {
* Ignore an exception * Ignore an exception
*/ */
async ignoreException(exception) { async ignoreException(exception) {
if (!confirm(I18n.t('marketplace.confirmations.ignore_exception'))) {
return;
}
try { try {
await apiClient.post(`/admin/order-exceptions/${exception.id}/ignore`, { await apiClient.post(`/admin/order-exceptions/${exception.id}/ignore`, {
notes: 'Ignored via admin interface' notes: 'Ignored via admin interface'

View File

@@ -66,6 +66,13 @@ function storeLetzshop() {
shipped: 0 shipped: 0
}, },
// Confirm modals
showDeleteCredentialsModal: false,
showConfirmOrderModal: false,
orderToConfirm: null,
showRejectOrderModal: false,
orderToReject: null,
// Modals // Modals
showTrackingModal: false, showTrackingModal: false,
showOrderModal: false, showOrderModal: false,
@@ -291,10 +298,6 @@ function storeLetzshop() {
* Delete credentials * Delete credentials
*/ */
async deleteCredentials() { async deleteCredentials() {
if (!confirm(I18n.t('marketplace.confirmations.remove_letzshop_credentials'))) {
return;
}
try { try {
await apiClient.delete('/store/letzshop/credentials'); await apiClient.delete('/store/letzshop/credentials');
this.credentials = null; this.credentials = null;
@@ -316,10 +319,6 @@ function storeLetzshop() {
* Confirm order * Confirm order
*/ */
async confirmOrder(order) { async confirmOrder(order) {
if (!confirm(I18n.t('marketplace.confirmations.confirm_order'))) {
return;
}
try { try {
const response = await apiClient.post(`/store/letzshop/orders/${order.id}/confirm`); const response = await apiClient.post(`/store/letzshop/orders/${order.id}/confirm`);
@@ -340,10 +339,6 @@ function storeLetzshop() {
* Reject order * Reject order
*/ */
async rejectOrder(order) { async rejectOrder(order) {
if (!confirm(I18n.t('marketplace.confirmations.reject_order'))) {
return;
}
try { try {
const response = await apiClient.post(`/store/letzshop/orders/${order.id}/reject`); const response = await apiClient.post(`/store/letzshop/orders/${order.id}/reject`);

View File

@@ -4,7 +4,7 @@
{% from 'shared/macros/headers.html' import page_header_flex, refresh_button %} {% from 'shared/macros/headers.html' import page_header_flex, refresh_button %}
{% from 'shared/macros/alerts.html' import error_state, alert_dynamic %} {% from 'shared/macros/alerts.html' import error_state, alert_dynamic %}
{% from 'shared/macros/tables.html' import table_wrapper, table_header %} {% from 'shared/macros/tables.html' import table_wrapper, table_header %}
{% from 'shared/macros/modals.html' import modal %} {% from 'shared/macros/modals.html' import modal, confirm_modal %}
{% block title %}Letzshop Management{% endblock %} {% block title %}Letzshop Management{% endblock %}
@@ -283,7 +283,7 @@
<button <button
type="button" type="button"
x-show="storeCredentials" x-show="storeCredentials"
@click="deleteStoreConfig()" @click="showDeleteStoreConfigModal = true"
class="px-4 py-2 text-sm font-medium text-red-600 border border-red-300 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20" class="px-4 py-2 text-sm font-medium text-red-600 border border-red-300 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20"
> >
Remove Remove
@@ -310,6 +310,9 @@
{% endcall %} {% endcall %}
<!-- Orders Modal --> <!-- Orders Modal -->
<!-- Delete Store Config Confirm Modal -->
{{ confirm_modal('deleteStoreConfigModal', 'Remove Configuration', 'Are you sure you want to remove the Letzshop configuration for this store? This will delete the API key and disable syncing.', 'deleteStoreConfig()', 'showDeleteStoreConfigModal', 'Remove', 'Cancel', 'danger') }}
{% call modal('ordersModal', 'Store Orders', 'showOrdersModal', size='xl') %} {% call modal('ordersModal', 'Store Orders', 'showOrdersModal', size='xl') %}
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4"> <p class="text-sm text-gray-500 dark:text-gray-400 mb-4">
Orders for: <span class="font-semibold" x-text="selectedStore?.store_name"></span> Orders for: <span class="font-semibold" x-text="selectedStore?.store_name"></span>

View File

@@ -4,7 +4,7 @@
{% from 'shared/macros/alerts.html' import alert_dynamic, error_state %} {% from 'shared/macros/alerts.html' import alert_dynamic, error_state %}
{% from 'shared/macros/headers.html' import page_header_flex, refresh_button %} {% from 'shared/macros/headers.html' import page_header_flex, refresh_button %}
{# Import modals macro - custom modals below use inline definition for specialized forms #} {# Import modals macro - custom modals below use inline definition for specialized forms #}
{% from 'shared/macros/modals.html' import modal_simple %} {% from 'shared/macros/modals.html' import modal_simple, confirm_modal %}
{% block title %}Letzshop Management{% endblock %} {% block title %}Letzshop Management{% endblock %}
{% block alpine_data %}adminMarketplaceLetzshop(){% endblock %} {% block alpine_data %}adminMarketplaceLetzshop(){% endblock %}
@@ -426,13 +426,13 @@
<!-- Bulk Actions --> <!-- Bulk Actions -->
<div x-show="selectedOrder?.status === 'pending'" class="mt-4 flex gap-2 justify-end"> <div x-show="selectedOrder?.status === 'pending'" class="mt-4 flex gap-2 justify-end">
<button <button
@click="confirmAllItems(selectedOrder)" @click="showConfirmAllItemsModal = true"
class="px-3 py-1.5 text-sm text-white bg-green-600 hover:bg-green-700 rounded-lg" class="px-3 py-1.5 text-sm text-white bg-green-600 hover:bg-green-700 rounded-lg"
> >
Confirm All Items Confirm All Items
</button> </button>
<button <button
@click="declineAllItems(selectedOrder)" @click="showDeclineAllItemsModal = true"
class="px-3 py-1.5 text-sm text-white bg-red-600 hover:bg-red-700 rounded-lg" class="px-3 py-1.5 text-sm text-white bg-red-600 hover:bg-red-700 rounded-lg"
> >
Decline All Items Decline All Items
@@ -582,6 +582,21 @@
</form> </form>
</div> </div>
</div> </div>
<!-- Decline Order Confirm Modal -->
{{ confirm_modal('declineOrderModal', 'Decline Order', 'Are you sure you want to decline this order? This action cannot be undone.', 'declineOrder(orderToDecline)', 'showDeclineOrderModal', 'Decline', 'Cancel', 'danger') }}
<!-- Confirm All Items Modal -->
{{ confirm_modal('confirmAllItemsModal', 'Confirm All Items', 'Are you sure you want to confirm all items in this order?', 'confirmAllItems(selectedOrder)', 'showConfirmAllItemsModal', 'Confirm All', 'Cancel', 'info') }}
<!-- Decline All Items Modal -->
{{ confirm_modal('declineAllItemsModal', 'Decline All Items', 'Are you sure you want to decline all items in this order? This action cannot be undone.', 'declineAllItems(selectedOrder)', 'showDeclineAllItemsModal', 'Decline All', 'Cancel', 'danger') }}
<!-- Delete Credentials Confirm Modal -->
{{ confirm_modal('deleteCredentialsModal', 'Remove Credentials', 'Are you sure you want to remove the Letzshop API credentials? This will disable order syncing for this store.', 'deleteCredentials()', 'showDeleteCredentialsModal', 'Remove', 'Cancel', 'danger') }}
<!-- Ignore Exception Confirm Modal -->
{{ confirm_modal('ignoreExceptionModal', 'Ignore Exception', 'Are you sure you want to ignore this exception? The unmatched product will not be resolved.', 'ignoreException(exceptionToIgnore)', 'showIgnoreExceptionModal', 'Ignore', 'Cancel', 'warning') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -186,7 +186,7 @@
</template> </template>
<template x-if="exc.status === 'pending'"> <template x-if="exc.status === 'pending'">
<button <button
@click="ignoreException(exc)" @click="exceptionToIgnore = exc; showIgnoreExceptionModal = true"
class="flex items-center justify-center px-2 py-1 text-sm text-gray-600 transition-colors duration-150 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700" class="flex items-center justify-center px-2 py-1 text-sm text-gray-600 transition-colors duration-150 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700"
title="Ignore Exception" title="Ignore Exception"
> >

View File

@@ -275,7 +275,7 @@
</button> </button>
<button <button
x-show="order.status === 'pending'" x-show="order.status === 'pending'"
@click="declineOrder(order)" @click="orderToDecline = order; showDeclineOrderModal = true"
class="flex items-center justify-center px-2 py-1 text-sm text-red-600 transition-colors duration-150 rounded-md hover:bg-red-100 dark:hover:bg-red-900" class="flex items-center justify-center px-2 py-1 text-sm text-red-600 transition-colors duration-150 rounded-md hover:bg-red-100 dark:hover:bg-red-900"
title="Decline Order" title="Decline Order"
> >

View File

@@ -148,7 +148,7 @@
<button <button
type="button" type="button"
x-show="credentials" x-show="credentials"
@click="deleteCredentials()" @click="showDeleteCredentialsModal = true"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-red-600 transition-colors duration-150 bg-white dark:bg-gray-800 border border-red-300 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 focus:outline-none" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-red-600 transition-colors duration-150 bg-white dark:bg-gray-800 border border-red-300 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 focus:outline-none"
> >
<span x-html="$icon('trash', 'w-4 h-4 mr-2')"></span> <span x-html="$icon('trash', 'w-4 h-4 mr-2')"></span>

View File

@@ -2,7 +2,7 @@
{% extends "store/base.html" %} {% extends "store/base.html" %}
{% from 'shared/macros/headers.html' import page_header_flex %} {% from 'shared/macros/headers.html' import page_header_flex %}
{% from 'shared/macros/tables.html' import table_wrapper, table_header %} {% from 'shared/macros/tables.html' import table_wrapper, table_header %}
{% from 'shared/macros/modals.html' import form_modal %} {% from 'shared/macros/modals.html' import form_modal, confirm_modal %}
{% block title %}Letzshop Orders{% endblock %} {% block title %}Letzshop Orders{% endblock %}
@@ -217,7 +217,7 @@
<div class="flex items-center space-x-2 text-sm"> <div class="flex items-center space-x-2 text-sm">
<button <button
x-show="order.sync_status === 'pending'" x-show="order.sync_status === 'pending'"
@click="confirmOrder(order)" @click="orderToConfirm = order; showConfirmOrderModal = true"
class="flex items-center justify-center px-2 py-1 text-sm text-green-600 transition-colors duration-150 rounded-md hover:bg-green-100 dark:hover:bg-green-900" class="flex items-center justify-center px-2 py-1 text-sm text-green-600 transition-colors duration-150 rounded-md hover:bg-green-100 dark:hover:bg-green-900"
title="Confirm Order" title="Confirm Order"
> >
@@ -225,7 +225,7 @@
</button> </button>
<button <button
x-show="order.sync_status === 'pending'" x-show="order.sync_status === 'pending'"
@click="rejectOrder(order)" @click="orderToReject = order; showRejectOrderModal = true"
class="flex items-center justify-center px-2 py-1 text-sm text-red-600 transition-colors duration-150 rounded-md hover:bg-red-100 dark:hover:bg-red-900" class="flex items-center justify-center px-2 py-1 text-sm text-red-600 transition-colors duration-150 rounded-md hover:bg-red-100 dark:hover:bg-red-900"
title="Reject Order" title="Reject Order"
> >
@@ -551,7 +551,7 @@
<button <button
type="button" type="button"
x-show="credentials" x-show="credentials"
@click="deleteCredentials()" @click="showDeleteCredentialsModal = true"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-red-600 transition-colors duration-150 bg-white dark:bg-gray-800 border border-red-300 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 focus:outline-none" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-red-600 transition-colors duration-150 bg-white dark:bg-gray-800 border border-red-300 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 focus:outline-none"
> >
<span x-html="$icon('trash', 'w-4 h-4 mr-2')"></span> <span x-html="$icon('trash', 'w-4 h-4 mr-2')"></span>
@@ -683,4 +683,13 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Delete Credentials Confirm Modal -->
{{ confirm_modal('deleteCredentialsModal', 'Remove Credentials', 'Are you sure you want to remove your Letzshop API credentials? This will disable order syncing.', 'deleteCredentials()', 'showDeleteCredentialsModal', 'Remove', 'Cancel', 'danger') }}
<!-- Confirm Order Modal -->
{{ confirm_modal('confirmOrderModal', 'Confirm Order', 'Are you sure you want to confirm this order? The order will be marked as accepted.', 'confirmOrder(orderToConfirm)', 'showConfirmOrderModal', 'Confirm', 'Cancel', 'info') }}
<!-- Reject Order Modal -->
{{ confirm_modal('rejectOrderModal', 'Reject Order', 'Are you sure you want to reject this order? This action cannot be undone.', 'rejectOrder(orderToReject)', 'showRejectOrderModal', 'Reject', 'Cancel', 'danger') }}
{% endblock %} {% endblock %}

View File

@@ -49,6 +49,9 @@ function adminMessages(initialConversationId = null) {
replyContent: '', replyContent: '',
attachedFiles: [], attachedFiles: [],
// Close conversation confirm state
showCloseConversationConfirm: false,
// Compose modal // Compose modal
showComposeModal: false, showComposeModal: false,
compose: { compose: {
@@ -304,8 +307,6 @@ function adminMessages(initialConversationId = null) {
* Close conversation * Close conversation
*/ */
async closeConversation() { async closeConversation() {
if (!confirm(I18n.t('messaging.confirmations.close_conversation_admin'))) return;
try { try {
await apiClient.post(`/admin/messages/${this.selectedConversationId}/close`); await apiClient.post(`/admin/messages/${this.selectedConversationId}/close`);

View File

@@ -67,6 +67,10 @@ function adminNotifications() {
resolvingAlert: null, resolvingAlert: null,
resolutionNotes: '', resolutionNotes: '',
// Delete notification confirm state
showDeleteNotificationConfirm: false,
pendingDeleteNotificationId: null,
/** /**
* Initialize component * Initialize component
*/ */
@@ -163,10 +167,6 @@ function adminNotifications() {
* Delete notification * Delete notification
*/ */
async deleteNotification(notificationId) { async deleteNotification(notificationId) {
if (!confirm(I18n.t('messaging.confirmations.delete_notification'))) {
return;
}
try { try {
await apiClient.delete(`/admin/notifications/${notificationId}`); await apiClient.delete(`/admin/notifications/${notificationId}`);

View File

@@ -43,6 +43,9 @@ function storeEmailTemplates() {
}, },
reverting: false, reverting: false,
// Revert confirm state
showRevertConfirm: false,
// Preview Modal // Preview Modal
showPreviewModal: false, showPreviewModal: false,
previewData: null, previewData: null,
@@ -184,10 +187,6 @@ function storeEmailTemplates() {
async revertToDefault() { async revertToDefault() {
if (!this.editingTemplate) return; if (!this.editingTemplate) return;
if (!confirm(I18n.t('messaging.confirmations.delete_customization'))) {
return;
}
this.reverting = true; this.reverting = true;
try { try {

View File

@@ -46,6 +46,9 @@ function storeMessages(initialConversationId = null) {
// Reply form // Reply form
replyContent: '', replyContent: '',
// Close conversation confirm state
showCloseConversationConfirm: false,
// Compose modal // Compose modal
showComposeModal: false, showComposeModal: false,
compose: { compose: {
@@ -279,8 +282,6 @@ function storeMessages(initialConversationId = null) {
* Close conversation * Close conversation
*/ */
async closeConversation() { async closeConversation() {
if (!confirm(I18n.t('messaging.confirmations.close_conversation'))) return;
try { try {
await apiClient.post(`/store/messages/${this.selectedConversationId}/close`); await apiClient.post(`/store/messages/${this.selectedConversationId}/close`);

View File

@@ -42,6 +42,10 @@ function storeNotifications() {
is_read: '' is_read: ''
}, },
// Delete notification confirm state
showDeleteNotificationConfirm: false,
pendingDeleteNotificationId: null,
// Settings // Settings
settings: null, settings: null,
showSettingsModal: false, showSettingsModal: false,
@@ -153,10 +157,6 @@ function storeNotifications() {
* Delete notification * Delete notification
*/ */
async deleteNotification(notificationId) { async deleteNotification(notificationId) {
if (!confirm(I18n.t('messaging.confirmations.delete_notification'))) {
return;
}
try { try {
await apiClient.delete(`/store/notifications/${notificationId}`); await apiClient.delete(`/store/notifications/${notificationId}`);

View File

@@ -2,7 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/headers.html' import page_header_flex %} {% from 'shared/macros/headers.html' import page_header_flex %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/modals.html' import form_modal %} {% from 'shared/macros/modals.html' import form_modal, confirm_modal %}
{% block title %}Messages{% endblock %} {% block title %}Messages{% endblock %}
@@ -157,7 +157,7 @@
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<template x-if="!selectedConversation.is_closed"> <template x-if="!selectedConversation.is_closed">
<button @click="closeConversation()" <button @click="showCloseConversationConfirm = true"
class="px-3 py-1.5 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"> class="px-3 py-1.5 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600">
Close Close
</button> </button>
@@ -329,6 +329,9 @@
</div> </div>
</div> </div>
{% endcall %} {% endcall %}
<!-- Close Conversation Confirm Modal -->
{{ confirm_modal('closeConversationConfirm', 'Close Conversation', 'Are you sure you want to close this conversation? You can reopen it later if needed.', 'closeConversation()', 'showCloseConversationConfirm', 'Close Conversation', 'Cancel', 'warning') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/modals.html' import confirm_modal %}
{% block title %}Notifications{% endblock %} {% block title %}Notifications{% endblock %}
@@ -213,7 +214,7 @@
Mark read Mark read
</button> </button>
</template> </template>
<button @click="deleteNotification(notif.id)" <button @click="pendingDeleteNotificationId = notif.id; showDeleteNotificationConfirm = true"
class="p-1 text-gray-400 hover:text-red-500 transition-colors"> class="p-1 text-gray-400 hover:text-red-500 transition-colors">
<span x-html="$icon('trash', 'w-4 h-4')"></span> <span x-html="$icon('trash', 'w-4 h-4')"></span>
</button> </button>
@@ -354,6 +355,9 @@
</ul> </ul>
</div> </div>
</div> </div>
<!-- Delete Notification Confirm Modal -->
{{ confirm_modal('deleteNotificationConfirm', 'Delete Notification', 'Are you sure you want to delete this notification? This action cannot be undone.', 'deleteNotification(pendingDeleteNotificationId)', 'showDeleteNotificationConfirm', 'Delete', 'Cancel', 'danger') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -2,7 +2,7 @@
{% extends "store/base.html" %} {% extends "store/base.html" %}
{% from 'shared/macros/headers.html' import page_header_flex %} {% from 'shared/macros/headers.html' import page_header_flex %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/modals.html' import modal_dialog %} {% from 'shared/macros/modals.html' import modal_dialog, confirm_modal %}
{% block title %}Email Templates{% endblock %} {% block title %}Email Templates{% endblock %}
@@ -212,7 +212,7 @@
<!-- Revert to Default Button --> <!-- Revert to Default Button -->
<template x-if="templateSource === 'store_override'"> <template x-if="templateSource === 'store_override'">
<button <button
@click="revertToDefault()" @click="showRevertConfirm = true"
:disabled="reverting" :disabled="reverting"
class="px-4 py-2 text-sm font-medium text-red-600 hover:text-red-700 hover:bg-red-50 rounded-lg dark:text-red-400 dark:hover:bg-red-900/20" class="px-4 py-2 text-sm font-medium text-red-600 hover:text-red-700 hover:bg-red-50 rounded-lg dark:text-red-400 dark:hover:bg-red-900/20"
> >
@@ -323,6 +323,9 @@
</div> </div>
</div> </div>
{% endcall %} {% endcall %}
<!-- Revert to Default Confirm Modal -->
{{ confirm_modal('revertConfirm', 'Revert to Platform Default', 'Are you sure you want to delete your customization and revert to the platform default template? This action cannot be undone.', 'revertToDefault()', 'showRevertConfirm', 'Revert', 'Cancel', 'danger') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -2,7 +2,7 @@
{% extends "store/base.html" %} {% extends "store/base.html" %}
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/modals.html' import modal_simple %} {% from 'shared/macros/modals.html' import modal_simple, confirm_modal %}
{% block title %}Messages{% endblock %} {% block title %}Messages{% endblock %}
@@ -128,7 +128,7 @@
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<template x-if="!selectedConversation.is_closed"> <template x-if="!selectedConversation.is_closed">
<button @click="closeConversation()" <button @click="showCloseConversationConfirm = true"
class="px-3 py-1.5 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"> class="px-3 py-1.5 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600">
Close Close
</button> </button>
@@ -272,6 +272,9 @@
</div> </div>
</form> </form>
{% endcall %} {% endcall %}
<!-- Close Conversation Confirm Modal -->
{{ confirm_modal('closeConversationConfirm', 'Close Conversation', 'Are you sure you want to close this conversation? You can reopen it later if needed.', 'closeConversation()', 'showCloseConversationConfirm', 'Close Conversation', 'Cancel', 'warning') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -3,7 +3,7 @@
{% from 'shared/macros/headers.html' import page_header_flex, refresh_button %} {% from 'shared/macros/headers.html' import page_header_flex, refresh_button %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/pagination.html' import pagination_simple %} {% from 'shared/macros/pagination.html' import pagination_simple %}
{% from 'shared/macros/modals.html' import modal_simple %} {% from 'shared/macros/modals.html' import modal_simple, confirm_modal %}
{% block title %}Notifications{% endblock %} {% block title %}Notifications{% endblock %}
@@ -145,7 +145,7 @@
Mark read Mark read
</button> </button>
</template> </template>
<button @click="deleteNotification(notif.id)" <button @click="pendingDeleteNotificationId = notif.id; showDeleteNotificationConfirm = true"
class="p-1 text-gray-400 hover:text-red-500 transition-colors"> class="p-1 text-gray-400 hover:text-red-500 transition-colors">
<span x-html="$icon('trash', 'w-4 h-4')"></span> <span x-html="$icon('trash', 'w-4 h-4')"></span>
</button> </button>
@@ -223,6 +223,9 @@
</button> </button>
</div> </div>
{% endcall %} {% endcall %}
<!-- Delete Notification Confirm Modal -->
{{ confirm_modal('deleteNotificationConfirm', 'Delete Notification', 'Are you sure you want to delete this notification? This action cannot be undone.', 'deleteNotification(pendingDeleteNotificationId)', 'showDeleteNotificationConfirm', 'Delete', 'Cancel', 'danger') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -3,7 +3,7 @@
{% from 'shared/macros/headers.html' import page_header_flex, refresh_button %} {% from 'shared/macros/headers.html' import page_header_flex, refresh_button %}
{% from 'shared/macros/tables.html' import table_wrapper, table_header, simple_pagination %} {% from 'shared/macros/tables.html' import table_wrapper, table_header, simple_pagination %}
{% from 'shared/macros/modals.html' import form_modal %} {% from 'shared/macros/modals.html' import form_modal, confirm_modal_dynamic %}
{% block title %}Invoices{% endblock %} {% block title %}Invoices{% endblock %}
@@ -219,7 +219,7 @@
</button> </button>
<button <button
x-show="invoice.status === 'draft'" x-show="invoice.status === 'draft'"
@click="updateStatus(invoice, 'issued')" @click="pendingStatusInvoice = invoice; pendingNewStatus = 'issued'; showUpdateStatusConfirm = true"
class="flex items-center justify-center px-2 py-1 text-sm text-blue-600 transition-colors duration-150 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900" class="flex items-center justify-center px-2 py-1 text-sm text-blue-600 transition-colors duration-150 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900"
title="Mark as Issued" title="Mark as Issued"
> >
@@ -227,7 +227,7 @@
</button> </button>
<button <button
x-show="invoice.status === 'issued'" x-show="invoice.status === 'issued'"
@click="updateStatus(invoice, 'paid')" @click="pendingStatusInvoice = invoice; pendingNewStatus = 'paid'; showUpdateStatusConfirm = true"
class="flex items-center justify-center px-2 py-1 text-sm text-green-600 transition-colors duration-150 rounded-md hover:bg-green-100 dark:hover:bg-green-900" class="flex items-center justify-center px-2 py-1 text-sm text-green-600 transition-colors duration-150 rounded-md hover:bg-green-100 dark:hover:bg-green-900"
title="Mark as Paid" title="Mark as Paid"
> >
@@ -235,7 +235,7 @@
</button> </button>
<button <button
x-show="invoice.status !== 'cancelled' && invoice.status !== 'paid'" x-show="invoice.status !== 'cancelled' && invoice.status !== 'paid'"
@click="updateStatus(invoice, 'cancelled')" @click="pendingStatusInvoice = invoice; pendingNewStatus = 'cancelled'; showUpdateStatusConfirm = true"
class="flex items-center justify-center px-2 py-1 text-sm text-red-600 transition-colors duration-150 rounded-md hover:bg-red-100 dark:hover:bg-red-900" class="flex items-center justify-center px-2 py-1 text-sm text-red-600 transition-colors duration-150 rounded-md hover:bg-red-100 dark:hover:bg-red-900"
title="Cancel Invoice" title="Cancel Invoice"
> >
@@ -467,6 +467,10 @@
</div> </div>
</div> </div>
<!-- Create Invoice Modal -->
<!-- Update Invoice Status Confirm Modal -->
{{ confirm_modal_dynamic('updateStatusConfirm', 'Update Invoice Status', "'Are you sure you want to ' + ({'issued': 'mark as issued', 'paid': 'mark as paid', 'cancelled': 'cancel'}[pendingNewStatus] || pendingNewStatus) + ' this invoice?'", 'updateStatus(pendingStatusInvoice, pendingNewStatus)', 'showUpdateStatusConfirm', 'Confirm', 'Cancel', 'warning') }}
<!-- Create Invoice Modal --> <!-- Create Invoice Modal -->
{% call form_modal('createInvoiceModal', 'Create Invoice', show_var='showCreateModal', submit_action='createInvoice()', submit_text='Create Invoice', loading_var='creatingInvoice', loading_text='Creating...', size='sm') %} {% call form_modal('createInvoiceModal', 'Create Invoice', show_var='showCreateModal', submit_action='createInvoice()', submit_text='Create Invoice', loading_var='creatingInvoice', loading_text='Creating...', size='sm') %}
<div class="mb-4"> <div class="mb-4">

View File

@@ -17,6 +17,9 @@ function adminUserDetailPage() {
error: null, error: null,
userId: null, userId: null,
currentUserId: null, currentUserId: null,
showToggleStatusModal: false,
showDeleteModal: false,
showDeleteFinalModal: false,
// Initialize // Initialize
async init() { async init() {
@@ -116,11 +119,6 @@ function adminUserDetailPage() {
return; return;
} }
if (!confirm(`Are you sure you want to ${action} "${this.adminUser.username}"?`)) {
adminUserDetailLog.info('Status toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/admin-users/${this.userId}/status`; const url = `/admin/admin-users/${this.userId}/status`;
@@ -142,6 +140,12 @@ function adminUserDetailPage() {
} }
}, },
// Intermediate step for double-confirm delete
confirmDeleteStep() {
adminUserDetailLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteFinalModal = true;
},
// Delete admin user // Delete admin user
async deleteAdminUser() { async deleteAdminUser() {
adminUserDetailLog.info('Delete admin user requested:', this.userId); adminUserDetailLog.info('Delete admin user requested:', this.userId);
@@ -152,17 +156,6 @@ function adminUserDetailPage() {
return; return;
} }
if (!confirm(`Are you sure you want to delete admin user "${this.adminUser.username}"?\n\nThis action cannot be undone.`)) {
adminUserDetailLog.info('Delete cancelled by user');
return;
}
// Second confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nAre you absolutely sure you want to delete "${this.adminUser.username}"?`)) {
adminUserDetailLog.info('Delete cancelled by user (second confirmation)');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/admin-users/${this.userId}`; const url = `/admin/admin-users/${this.userId}`;

View File

@@ -26,6 +26,10 @@ function adminUserEditPage() {
// Confirmation modal state // Confirmation modal state
showRemovePlatformModal: false, showRemovePlatformModal: false,
platformToRemove: null, platformToRemove: null,
showToggleSuperAdminModal: false,
showToggleStatusModal: false,
showDeleteModal: false,
showDeleteFinalModal: false,
// Initialize // Initialize
async init() { async init() {
@@ -149,11 +153,6 @@ function adminUserEditPage() {
return; return;
} }
if (!confirm(`Are you sure you want to ${action} super admin "${this.adminUser.username}"?`)) {
adminUserEditLog.info('Super admin toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/admin-users/${this.userId}/super-admin`; const url = `/admin/admin-users/${this.userId}/super-admin`;
@@ -192,11 +191,6 @@ function adminUserEditPage() {
return; return;
} }
if (!confirm(`Are you sure you want to ${action} "${this.adminUser.username}"?`)) {
adminUserEditLog.info('Status toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/admin-users/${this.userId}/status`; const url = `/admin/admin-users/${this.userId}/status`;
@@ -298,6 +292,12 @@ function adminUserEditPage() {
this.selectedPlatformId = null; this.selectedPlatformId = null;
}, },
// Intermediate step for double-confirm delete
confirmDeleteStep() {
adminUserEditLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteFinalModal = true;
},
// Delete admin user // Delete admin user
async deleteAdminUser() { async deleteAdminUser() {
adminUserEditLog.info('Delete admin user requested:', this.userId); adminUserEditLog.info('Delete admin user requested:', this.userId);
@@ -308,17 +308,6 @@ function adminUserEditPage() {
return; return;
} }
if (!confirm(`Are you sure you want to delete admin user "${this.adminUser.username}"?\n\nThis action cannot be undone.`)) {
adminUserEditLog.info('Delete cancelled by user');
return;
}
// Second confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nAre you absolutely sure you want to delete "${this.adminUser.username}"?`)) {
adminUserEditLog.info('Delete cancelled by user (second confirmation)');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/admin-users/${this.userId}`; const url = `/admin/admin-users/${this.userId}`;

View File

@@ -17,6 +17,9 @@ function adminUsersPage() {
loading: false, loading: false,
error: null, error: null,
currentUserId: null, currentUserId: null,
showDeleteModal: false,
showDeleteFinalModal: false,
adminToDelete: null,
filters: { filters: {
search: '', search: '',
is_super_admin: '', is_super_admin: '',
@@ -286,6 +289,12 @@ function adminUsersPage() {
window.location.href = `/admin/admin-users/${admin.id}/edit`; window.location.href = `/admin/admin-users/${admin.id}/edit`;
}, },
// Intermediate step for double-confirm delete
confirmDeleteStep() {
adminUsersLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteFinalModal = true;
},
async deleteAdminUser(admin) { async deleteAdminUser(admin) {
adminUsersLog.warn('Delete admin user requested:', admin.username); adminUsersLog.warn('Delete admin user requested:', admin.username);
@@ -295,17 +304,6 @@ function adminUsersPage() {
return; return;
} }
if (!confirm(`Are you sure you want to delete admin user "${admin.username}"?\n\nThis action cannot be undone.`)) {
adminUsersLog.info('Delete cancelled by user');
return;
}
// Second confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nAre you absolutely sure you want to delete "${admin.username}"?`)) {
adminUsersLog.info('Delete cancelled by user (second confirmation)');
return;
}
try { try {
const url = `/admin/admin-users/${admin.id}`; const url = `/admin/admin-users/${admin.id}`;
window.LogConfig.logApiCall('DELETE', url, null, 'request'); window.LogConfig.logApiCall('DELETE', url, null, 'request');

View File

@@ -16,6 +16,10 @@ function adminMerchantDetail() {
error: null, error: null,
merchantId: null, merchantId: null,
// Modal state
showDeleteMerchantModal: false,
showDeleteMerchantFinalModal: false,
// Subscription state // Subscription state
platforms: [], platforms: [],
subscriptions: [], subscriptions: [],
@@ -239,8 +243,8 @@ function adminMerchantDetail() {
return formatted; return formatted;
}, },
// Delete merchant // Prompt delete merchant (first step of double confirm)
async deleteMerchant() { promptDeleteMerchant() {
merchantDetailLog.info('Delete merchant requested:', this.merchantId); merchantDetailLog.info('Delete merchant requested:', this.merchantId);
if (this.merchant?.store_count > 0) { if (this.merchant?.store_count > 0) {
@@ -248,17 +252,16 @@ function adminMerchantDetail() {
return; return;
} }
if (!confirm(`Are you sure you want to delete merchant "${this.merchant.name}"?\n\nThis action cannot be undone.`)) { this.showDeleteMerchantModal = true;
merchantDetailLog.info('Delete cancelled by user'); },
return;
}
// Second confirmation for safety // Confirm first step, show final confirmation
if (!confirm(`FINAL CONFIRMATION\n\nAre you absolutely sure you want to delete "${this.merchant.name}"?`)) { confirmDeleteMerchantStep() {
merchantDetailLog.info('Delete cancelled by user (second confirmation)'); this.showDeleteMerchantFinalModal = true;
return; },
}
// Delete merchant
async deleteMerchant() {
try { try {
const url = `/admin/merchants/${this.merchantId}?confirm=true`; const url = `/admin/merchants/${this.merchantId}?confirm=true`;
window.LogConfig.logApiCall('DELETE', url, null, 'request'); window.LogConfig.logApiCall('DELETE', url, null, 'request');

View File

@@ -18,6 +18,12 @@ function adminMerchantEdit() {
saving: false, saving: false,
merchantId: null, merchantId: null,
// Modal state
showToggleVerificationModal: false,
showToggleActiveModal: false,
showDeleteMerchantModal: false,
showDeleteMerchantFinalModal: false,
// Transfer ownership state // Transfer ownership state
showTransferOwnershipModal: false, showTransferOwnershipModal: false,
transferring: false, transferring: false,
@@ -166,11 +172,6 @@ function adminMerchantEdit() {
const action = this.merchant.is_verified ? 'unverify' : 'verify'; const action = this.merchant.is_verified ? 'unverify' : 'verify';
merchantEditLog.info(`Toggle verification: ${action}`); merchantEditLog.info(`Toggle verification: ${action}`);
if (!confirm(`Are you sure you want to ${action} this merchant?`)) {
merchantEditLog.info('Verification toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/merchants/${this.merchantId}/verification`; const url = `/admin/merchants/${this.merchantId}/verification`;
@@ -199,11 +200,6 @@ function adminMerchantEdit() {
const action = this.merchant.is_active ? 'deactivate' : 'activate'; const action = this.merchant.is_active ? 'deactivate' : 'activate';
merchantEditLog.info(`Toggle active status: ${action}`); merchantEditLog.info(`Toggle active status: ${action}`);
if (!confirm(`Are you sure you want to ${action} this merchant?\n\nThis will affect all stores under this merchant.`)) {
merchantEditLog.info('Active status toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/merchants/${this.merchantId}/status`; const url = `/admin/merchants/${this.merchantId}/status`;
@@ -343,8 +339,8 @@ function adminMerchantEdit() {
this.userSearchResults = []; this.userSearchResults = [];
}, },
// Delete merchant // Prompt delete merchant (first step of double confirm)
async deleteMerchant() { promptDeleteMerchant() {
merchantEditLog.info('=== DELETING MERCHANT ==='); merchantEditLog.info('=== DELETING MERCHANT ===');
if (this.merchant.store_count > 0) { if (this.merchant.store_count > 0) {
@@ -352,16 +348,17 @@ function adminMerchantEdit() {
return; return;
} }
if (!confirm(`Are you sure you want to delete merchant "${this.merchant.name}"?\n\nThis action cannot be undone.`)) { this.showDeleteMerchantModal = true;
merchantEditLog.info('Merchant deletion cancelled by user'); },
return;
}
// Double confirmation for critical action // Confirm first step, show final confirmation
if (!confirm(`FINAL CONFIRMATION: Delete "${this.merchant.name}"?\n\nThis will permanently delete the merchant and all its data.`)) { confirmDeleteMerchantStep() {
merchantEditLog.info('Merchant deletion cancelled at final confirmation'); this.showDeleteMerchantFinalModal = true;
return; },
}
// Delete merchant
async deleteMerchant() {
merchantEditLog.info('=== DELETING MERCHANT (confirmed) ===');
this.saving = true; this.saving = true;
try { try {

View File

@@ -16,6 +16,9 @@ function merchantUserDetailPage() {
saving: false, saving: false,
error: null, error: null,
userId: null, userId: null,
showToggleStatusModal: false,
showDeleteModal: false,
showDeleteFinalModal: false,
// Initialize // Initialize
async init() { async init() {
@@ -100,11 +103,6 @@ function merchantUserDetailPage() {
const action = this.merchantUser.is_active ? 'deactivate' : 'activate'; const action = this.merchantUser.is_active ? 'deactivate' : 'activate';
merchantUserDetailLog.info(`Toggle status: ${action}`); merchantUserDetailLog.info(`Toggle status: ${action}`);
if (!confirm(`Are you sure you want to ${action} "${this.merchantUser.full_name || this.merchantUser.username}"?`)) {
merchantUserDetailLog.info('Status toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/users/${this.userId}/status`; const url = `/admin/users/${this.userId}/status`;
@@ -126,21 +124,16 @@ function merchantUserDetailPage() {
} }
}, },
// Intermediate step for double-confirm delete
confirmDeleteStep() {
merchantUserDetailLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteFinalModal = true;
},
// Delete user // Delete user
async deleteUser() { async deleteUser() {
merchantUserDetailLog.info('Delete user requested:', this.userId); merchantUserDetailLog.info('Delete user requested:', this.userId);
if (!confirm(`Are you sure you want to delete "${this.merchantUser.full_name || this.merchantUser.username}"?\n\nThis action cannot be undone.`)) {
merchantUserDetailLog.info('Delete cancelled by user');
return;
}
// Second confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nAre you absolutely sure you want to delete "${this.merchantUser.full_name || this.merchantUser.username}"?`)) {
merchantUserDetailLog.info('Delete cancelled by user (second confirmation)');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/users/${this.userId}`; const url = `/admin/users/${this.userId}`;

View File

@@ -16,6 +16,11 @@ function merchantUsersPage() {
merchantUsers: [], merchantUsers: [],
loading: false, loading: false,
error: null, error: null,
showToggleStatusModal: false,
showDeleteModal: false,
showDeleteFinalModal: false,
userToToggle: null,
userToDelete: null,
filters: { filters: {
search: '', search: '',
is_active: '' is_active: ''
@@ -230,11 +235,6 @@ function merchantUsersPage() {
const action = user.is_active ? 'deactivate' : 'activate'; const action = user.is_active ? 'deactivate' : 'activate';
merchantUsersLog.info(`Toggle status: ${action} for user`, user.username); merchantUsersLog.info(`Toggle status: ${action} for user`, user.username);
if (!confirm(`Are you sure you want to ${action} "${user.full_name || user.username || user.email}"?`)) {
merchantUsersLog.info('Status toggle cancelled by user');
return;
}
try { try {
const url = `/admin/users/${user.id}/status`; const url = `/admin/users/${user.id}/status`;
window.LogConfig.logApiCall('PUT', url, null, 'request'); window.LogConfig.logApiCall('PUT', url, null, 'request');
@@ -254,21 +254,16 @@ function merchantUsersPage() {
} }
}, },
// Intermediate step for double-confirm delete
confirmDeleteStep() {
merchantUsersLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteFinalModal = true;
},
// Delete user // Delete user
async deleteUser(user) { async deleteUser(user) {
merchantUsersLog.warn('Delete user requested:', user.username); merchantUsersLog.warn('Delete user requested:', user.username);
if (!confirm(`Are you sure you want to delete "${user.full_name || user.username || user.email}"?\n\nThis action cannot be undone.`)) {
merchantUsersLog.info('Delete cancelled by user');
return;
}
// Second confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nAre you absolutely sure you want to delete "${user.full_name || user.username || user.email}"?`)) {
merchantUsersLog.info('Delete cancelled by user (second confirmation)');
return;
}
try { try {
const url = `/admin/users/${user.id}`; const url = `/admin/users/${user.id}`;
window.LogConfig.logApiCall('DELETE', url, null, 'request'); window.LogConfig.logApiCall('DELETE', url, null, 'request');

View File

@@ -26,6 +26,10 @@ function adminMerchants() {
loading: false, loading: false,
error: null, error: null,
// Modal state
showDeleteMerchantModal: false,
merchantToDelete: null,
// Search and filters // Search and filters
filters: { filters: {
search: '', search: '',
@@ -192,23 +196,19 @@ function adminMerchants() {
window.location.href = `/admin/merchants/${merchantId}/edit`; window.location.href = `/admin/merchants/${merchantId}/edit`;
}, },
// Delete merchant // Prompt delete merchant modal
async deleteMerchant(merchant) { promptDeleteMerchant(merchant) {
if (merchant.store_count > 0) { if (merchant.store_count > 0) {
merchantsLog.warn('Cannot delete merchant with stores'); merchantsLog.warn('Cannot delete merchant with stores');
Utils.showToast(`Cannot delete "${merchant.name}" because it has ${merchant.store_count} store(s). Please delete or reassign the stores first.`, 'warning'); Utils.showToast(`Cannot delete "${merchant.name}" because it has ${merchant.store_count} store(s). Please delete or reassign the stores first.`, 'warning');
return; return;
} }
this.merchantToDelete = merchant;
this.showDeleteMerchantModal = true;
},
const confirmed = confirm( // Delete merchant
`Are you sure you want to delete "${merchant.name}"?\n\nThis action cannot be undone.` async deleteMerchant(merchant) {
);
if (!confirmed) {
merchantsLog.info('Delete cancelled by user');
return;
}
try { try {
merchantsLog.info('Deleting merchant:', merchant.id); merchantsLog.info('Deleting merchant:', merchant.id);

View File

@@ -18,6 +18,8 @@ function adminPlatformMenuConfig(platformCode) {
error: null, error: null,
successMessage: null, successMessage: null,
saving: false, saving: false,
showShowAllModal: false,
showHideAllModal: false,
// Data // Data
platform: null, platform: null,
@@ -160,10 +162,6 @@ function adminPlatformMenuConfig(platformCode) {
}, },
async showAll() { async showAll() {
if (!confirm(I18n.t('tenancy.confirmations.show_all_menu_items'))) {
return;
}
this.saving = true; this.saving = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;
@@ -186,10 +184,6 @@ function adminPlatformMenuConfig(platformCode) {
}, },
async resetToDefaults() { async resetToDefaults() {
if (!confirm(I18n.t('tenancy.confirmations.hide_all_menu_items'))) {
return;
}
this.saving = true; this.saving = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;

View File

@@ -15,6 +15,8 @@ function adminPlatformModules(platformCode) {
error: null, error: null,
successMessage: null, successMessage: null,
saving: false, saving: false,
showEnableAllModal: false,
showDisableOptionalModal: false,
// Data // Data
platform: null, platform: null,
@@ -176,10 +178,6 @@ function adminPlatformModules(platformCode) {
}, },
async enableAll() { async enableAll() {
if (!confirm(I18n.t('tenancy.confirmations.enable_all_modules'))) {
return;
}
this.saving = true; this.saving = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;
@@ -207,10 +205,6 @@ function adminPlatformModules(platformCode) {
}, },
async disableOptional() { async disableOptional() {
if (!confirm(I18n.t('tenancy.confirmations.disable_optional_modules'))) {
return;
}
this.saving = true; this.saving = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;

View File

@@ -17,6 +17,8 @@ function adminStoreDetail() {
loading: false, loading: false,
error: null, error: null,
storeCode: null, storeCode: null,
showDeleteStoreModal: false,
showDeleteStoreFinalModal: false,
// Initialize // Initialize
async init() { async init() {
@@ -143,21 +145,20 @@ function adminStoreDetail() {
return 'bg-green-500'; return 'bg-green-500';
}, },
// Prompt delete store (first step of double confirm)
promptDeleteStore() {
detailLog.info('Delete store requested:', this.storeCode);
this.showDeleteStoreModal = true;
},
// Confirm first step, show final confirmation
confirmDeleteStoreStep() {
detailLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteStoreFinalModal = true;
},
// Delete store // Delete store
async deleteStore() { async deleteStore() {
detailLog.info('Delete store requested:', this.storeCode);
if (!confirm(`Are you sure you want to delete store "${this.store.name}"?\n\nThis action cannot be undone and will delete:\n- All products\n- All orders\n- All customers\n- All team members`)) {
detailLog.info('Delete cancelled by user');
return;
}
// Second confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nType the store code to confirm: ${this.store.store_code}\n\nAre you absolutely sure?`)) {
detailLog.info('Delete cancelled by user (second confirmation)');
return;
}
try { try {
const url = `/admin/stores/${this.storeCode}?confirm=true`; const url = `/admin/stores/${this.storeCode}?confirm=true`;
window.LogConfig.logApiCall('DELETE', url, null, 'request'); window.LogConfig.logApiCall('DELETE', url, null, 'request');
@@ -189,4 +190,4 @@ function adminStoreDetail() {
}; };
} }
detailLog.info('Store detail module loaded'); detailLog.info('Store detail module loaded');

View File

@@ -18,6 +18,10 @@ function adminStoreEdit() {
loadingStore: false, loadingStore: false,
saving: false, saving: false,
storeCode: null, storeCode: null,
showToggleVerificationModal: false,
showToggleActiveModal: false,
showDeleteStoreModal: false,
showDeleteStoreFinalModal: false,
// Initialize // Initialize
async init() { async init() {
@@ -166,11 +170,6 @@ function adminStoreEdit() {
const action = this.store.is_verified ? 'unverify' : 'verify'; const action = this.store.is_verified ? 'unverify' : 'verify';
editLog.info(`Toggle verification: ${action}`); editLog.info(`Toggle verification: ${action}`);
if (!confirm(`Are you sure you want to ${action} this store?`)) {
editLog.info('Verification toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/stores/${this.storeCode}/verification`; const url = `/admin/stores/${this.storeCode}/verification`;
@@ -199,11 +198,6 @@ function adminStoreEdit() {
const action = this.store.is_active ? 'deactivate' : 'activate'; const action = this.store.is_active ? 'deactivate' : 'activate';
editLog.info(`Toggle active status: ${action}`); editLog.info(`Toggle active status: ${action}`);
if (!confirm(`Are you sure you want to ${action} this store?\n\nThis will affect their operations.`)) {
editLog.info('Active status toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/stores/${this.storeCode}/status`; const url = `/admin/stores/${this.storeCode}/status`;
@@ -227,22 +221,20 @@ function adminStoreEdit() {
} }
}, },
// Prompt delete store (first step of double confirm)
promptDeleteStore() {
editLog.info('Delete store requested');
this.showDeleteStoreModal = true;
},
// Confirm first step, show final confirmation
confirmDeleteStoreStep() {
editLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteStoreFinalModal = true;
},
// Delete store // Delete store
async deleteStore() { async deleteStore() {
editLog.info('Delete store requested');
const storeName = this.store?.name || this.storeCode;
if (!confirm(`Are you sure you want to delete "${storeName}"?\n\n⚠️ WARNING: This will permanently delete:\n• All products\n• All orders\n• All customers\n• All team members\n\nThis action cannot be undone!`)) {
editLog.info('Delete cancelled by user');
return;
}
// Double confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nType OK to permanently delete "${storeName}" and ALL associated data.`)) {
editLog.info('Delete cancelled at final confirmation');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/stores/${this.storeCode}?confirm=true`; const url = `/admin/stores/${this.storeCode}?confirm=true`;
@@ -311,4 +303,4 @@ function adminStoreEdit() {
}; };
} }
editLog.info('Store edit module loaded'); editLog.info('Store edit module loaded');

View File

@@ -31,6 +31,7 @@ function adminStoreTheme() {
loading: true, loading: true,
saving: false, saving: false,
error: null, error: null,
showResetThemeModal: false,
// Theme data structure matching StoreTheme model // Theme data structure matching StoreTheme model
themeData: { themeData: {
@@ -272,10 +273,6 @@ function adminStoreTheme() {
}, },
async resetTheme() { async resetTheme() {
if (!confirm(I18n.t('tenancy.confirmations.reset_theme'))) {
return;
}
themeLog.warn('Resetting theme to default'); themeLog.warn('Resetting theme to default');
this.saving = true; this.saving = true;
@@ -332,4 +329,4 @@ function adminStoreTheme() {
// MODULE LOADED // MODULE LOADED
// ============================================================================ // ============================================================================
themeLog.info('Store theme editor module loaded'); themeLog.info('Store theme editor module loaded');

View File

@@ -25,6 +25,8 @@ function adminStores() {
}, },
loading: false, loading: false,
error: null, error: null,
showDeleteStoreModal: false,
storeToDelete: null,
// Search and filters // Search and filters
filters: { filters: {
@@ -283,15 +285,15 @@ function adminStores() {
window.location.href = url; window.location.href = url;
}, },
// Prompt delete store
promptDeleteStore(store) {
storesLog.info('Delete store requested:', store.store_code);
this.storeToDelete = store;
this.showDeleteStoreModal = true;
},
// Delete store // Delete store
async deleteStore(store) { async deleteStore(store) {
storesLog.info('Delete store requested:', store.store_code);
if (!confirm(`Are you sure you want to delete store "${store.name}"?\n\nThis action cannot be undone.`)) {
storesLog.info('Delete cancelled by user');
return;
}
try { try {
const url = `/admin/stores/${store.store_code}`; const url = `/admin/stores/${store.store_code}`;
window.LogConfig.logApiCall('DELETE', url, null, 'request'); window.LogConfig.logApiCall('DELETE', url, null, 'request');
@@ -329,4 +331,4 @@ function adminStores() {
}; };
} }
storesLog.info('Stores module loaded'); storesLog.info('Stores module loaded');

View File

@@ -16,6 +16,9 @@ function adminUserDetail() {
saving: false, saving: false,
error: null, error: null,
userId: null, userId: null,
showToggleStatusModal: false,
showDeleteModal: false,
showDeleteFinalModal: false,
// Initialize // Initialize
async init() { async init() {
@@ -97,11 +100,6 @@ function adminUserDetail() {
const action = this.user.is_active ? 'deactivate' : 'activate'; const action = this.user.is_active ? 'deactivate' : 'activate';
userDetailLog.info(`Toggle status: ${action}`); userDetailLog.info(`Toggle status: ${action}`);
if (!confirm(`Are you sure you want to ${action} ${this.user.username}?`)) {
userDetailLog.info('Status toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/users/${this.userId}/status`; const url = `/admin/users/${this.userId}/status`;
@@ -123,6 +121,12 @@ function adminUserDetail() {
} }
}, },
// Intermediate step for double-confirm delete
confirmDeleteStep() {
userDetailLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteFinalModal = true;
},
// Delete user // Delete user
async deleteUser() { async deleteUser() {
userDetailLog.info('Delete user requested:', this.userId); userDetailLog.info('Delete user requested:', this.userId);
@@ -132,17 +136,6 @@ function adminUserDetail() {
return; return;
} }
if (!confirm(`Are you sure you want to delete "${this.user.username}"?\n\nThis action cannot be undone.`)) {
userDetailLog.info('Delete cancelled by user');
return;
}
// Second confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nAre you absolutely sure you want to delete "${this.user.username}"?`)) {
userDetailLog.info('Delete cancelled by user (second confirmation)');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/users/${this.userId}`; const url = `/admin/users/${this.userId}`;

View File

@@ -17,6 +17,9 @@ function adminUserEdit() {
loadingUser: false, loadingUser: false,
saving: false, saving: false,
userId: null, userId: null,
showToggleStatusModal: false,
showDeleteModal: false,
showDeleteFinalModal: false,
// Initialize // Initialize
async init() { async init() {
@@ -154,11 +157,6 @@ function adminUserEdit() {
const action = this.user.is_active ? 'deactivate' : 'activate'; const action = this.user.is_active ? 'deactivate' : 'activate';
userEditLog.info(`Toggle status: ${action}`); userEditLog.info(`Toggle status: ${action}`);
if (!confirm(`Are you sure you want to ${action} ${this.user.username}?`)) {
userEditLog.info('Status toggle cancelled by user');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/users/${this.userId}/status`; const url = `/admin/users/${this.userId}/status`;
@@ -180,6 +178,12 @@ function adminUserEdit() {
} }
}, },
// Intermediate step for double-confirm delete
confirmDeleteStep() {
userEditLog.info('First delete confirmation accepted, showing final confirmation');
this.showDeleteFinalModal = true;
},
// Delete user // Delete user
async deleteUser() { async deleteUser() {
userEditLog.info('=== DELETING USER ==='); userEditLog.info('=== DELETING USER ===');
@@ -189,17 +193,6 @@ function adminUserEdit() {
return; return;
} }
if (!confirm(`Are you sure you want to delete user "${this.user.username}"?\n\nThis action cannot be undone.`)) {
userEditLog.info('User deletion cancelled by user');
return;
}
// Double confirmation for critical action
if (!confirm(`FINAL CONFIRMATION: Delete "${this.user.username}"?\n\nThis will permanently delete the user.`)) {
userEditLog.info('User deletion cancelled at final confirmation');
return;
}
this.saving = true; this.saving = true;
try { try {
const url = `/admin/users/${this.userId}`; const url = `/admin/users/${this.userId}`;

View File

@@ -16,6 +16,10 @@ function adminUsers() {
users: [], users: [],
loading: false, loading: false,
error: null, error: null,
showToggleStatusModal: false,
showDeleteModal: false,
userToToggle: null,
userToDelete: null,
filters: { filters: {
search: '', search: '',
role: '', role: '',
@@ -171,15 +175,15 @@ function adminUsers() {
// Load statistics // Load statistics
async loadStats() { async loadStats() {
usersLog.info('Loading user statistics...'); usersLog.info('Loading user statistics...');
try { try {
const url = '/admin/users/stats'; const url = '/admin/users/stats';
window.LogConfig.logApiCall('GET', url, null, 'request'); window.LogConfig.logApiCall('GET', url, null, 'request');
const response = await apiClient.get(url); // ✅ Fixed: lowercase apiClient const response = await apiClient.get(url); // ✅ Fixed: lowercase apiClient
window.LogConfig.logApiCall('GET', url, response, 'response'); window.LogConfig.logApiCall('GET', url, response, 'response');
if (response) { if (response) {
this.stats = response; this.stats = response;
usersLog.debug('Stats loaded:', this.stats); usersLog.debug('Stats loaded:', this.stats);
@@ -242,23 +246,18 @@ function adminUsers() {
async toggleUserStatus(user) { async toggleUserStatus(user) {
const action = user.is_active ? 'deactivate' : 'activate'; const action = user.is_active ? 'deactivate' : 'activate';
usersLog.info(`Toggle user status: ${action}`, user.username); usersLog.info(`Toggle user status: ${action}`, user.username);
if (!confirm(`Are you sure you want to ${action} ${user.username}?`)) {
usersLog.info('Status toggle cancelled by user');
return;
}
try { try {
const url = `/admin/users/${user.id}/status`; const url = `/admin/users/${user.id}/status`;
window.LogConfig.logApiCall('PUT', url, { is_active: !user.is_active }, 'request'); window.LogConfig.logApiCall('PUT', url, { is_active: !user.is_active }, 'request');
await apiClient.put(url, { // ✅ Fixed: lowercase apiClient await apiClient.put(url, { // ✅ Fixed: lowercase apiClient
is_active: !user.is_active is_active: !user.is_active
}); });
Utils.showToast(`User ${action}d successfully`, 'success'); Utils.showToast(`User ${action}d successfully`, 'success');
usersLog.info(`User ${action}d successfully`); usersLog.info(`User ${action}d successfully`);
await this.loadUsers(); await this.loadUsers();
await this.loadStats(); await this.loadStats();
} catch (error) { } catch (error) {
@@ -269,21 +268,16 @@ function adminUsers() {
async deleteUser(user) { async deleteUser(user) {
usersLog.warn('Delete user requested:', user.username); usersLog.warn('Delete user requested:', user.username);
if (!confirm(`Are you sure you want to delete ${user.username}? This action cannot be undone.`)) {
usersLog.info('Delete cancelled by user');
return;
}
try { try {
const url = `/admin/users/${user.id}`; const url = `/admin/users/${user.id}`;
window.LogConfig.logApiCall('DELETE', url, null, 'request'); window.LogConfig.logApiCall('DELETE', url, null, 'request');
await apiClient.delete(url); // ✅ Fixed: lowercase apiClient await apiClient.delete(url); // ✅ Fixed: lowercase apiClient
Utils.showToast(I18n.t('tenancy.messages.user_deleted_successfully'), 'success'); Utils.showToast(I18n.t('tenancy.messages.user_deleted_successfully'), 'success');
usersLog.info('User deleted successfully'); usersLog.info('User deleted successfully');
await this.loadUsers(); await this.loadUsers();
await this.loadStats(); await this.loadStats();
} catch (error) { } catch (error) {
@@ -299,4 +293,4 @@ function adminUsers() {
}; };
} }
usersLog.info('Users module loaded'); usersLog.info('Users module loaded');

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/headers.html' import detail_page_header %} {% from 'shared/macros/headers.html' import detail_page_header %}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
{% block title %}Admin User Details{% endblock %} {% block title %}Admin User Details{% endblock %}
@@ -33,7 +34,7 @@
Edit Admin User Edit Admin User
</a> </a>
<button <button
@click="toggleStatus()" @click="showToggleStatusModal = true"
:disabled="saving || adminUser?.id === currentUserId" :disabled="saving || adminUser?.id === currentUserId"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 border border-transparent rounded-lg focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 border border-transparent rounded-lg focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed"
:class="adminUser?.is_active ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'" :class="adminUser?.is_active ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'"
@@ -42,7 +43,7 @@
<span x-text="adminUser?.is_active ? 'Deactivate' : 'Activate'"></span> <span x-text="adminUser?.is_active ? 'Deactivate' : 'Activate'"></span>
</button> </button>
<button <button
@click="deleteAdminUser()" @click="showDeleteModal = true"
:disabled="saving || adminUser?.id === currentUserId" :disabled="saving || adminUser?.id === currentUserId"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50 disabled:cursor-not-allowed" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50 disabled:cursor-not-allowed"
:title="adminUser?.id === currentUserId ? 'Cannot delete yourself' : 'Delete admin user'"> :title="adminUser?.id === currentUserId ? 'Cannot delete yourself' : 'Delete admin user'">
@@ -231,6 +232,42 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Toggle Status Confirmation Modal -->
{{ confirm_modal_dynamic(
'toggleStatusModal',
'Toggle User Status',
"'Are you sure you want to ' + (adminUser?.is_active ? 'deactivate' : 'activate') + ' \"' + (adminUser?.username || '') + '\"?'",
'toggleStatus()',
'showToggleStatusModal',
'Confirm',
'Cancel',
'warning'
) }}
<!-- Delete Admin User Confirmation Modal (Step 1) -->
{{ confirm_modal_dynamic(
'deleteAdminUserModal',
'Delete Admin User',
"'Are you sure you want to delete admin user \"' + (adminUser?.username || '') + '\"? This action cannot be undone.'",
'confirmDeleteStep()',
'showDeleteModal',
'Delete',
'Cancel',
'danger'
) }}
<!-- Delete Admin User Final Confirmation Modal (Step 2) -->
{{ confirm_modal_dynamic(
'deleteAdminUserFinalModal',
'Final Confirmation',
"'FINAL CONFIRMATION: Are you absolutely sure you want to permanently delete \"' + (adminUser?.username || '') + '\"?'",
'deleteAdminUser()',
'showDeleteFinalModal',
'Permanently Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -25,7 +25,7 @@
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-3">
<!-- Toggle Active Status --> <!-- Toggle Active Status -->
<button <button
@click="toggleStatus()" @click="showToggleStatusModal = true"
:disabled="saving || adminUser?.id === currentUserId" :disabled="saving || adminUser?.id === currentUserId"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none disabled:opacity-50" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none disabled:opacity-50"
:class="adminUser?.is_active ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'" :class="adminUser?.is_active ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'"
@@ -36,7 +36,7 @@
<!-- Toggle Super Admin --> <!-- Toggle Super Admin -->
<button <button
@click="toggleSuperAdmin()" @click="showToggleSuperAdminModal = true"
:disabled="saving || (adminUser?.id === currentUserId && adminUser?.is_super_admin)" :disabled="saving || (adminUser?.id === currentUserId && adminUser?.is_super_admin)"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none disabled:opacity-50" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none disabled:opacity-50"
:class="adminUser?.is_super_admin ? 'bg-yellow-600 hover:bg-yellow-700' : 'bg-purple-600 hover:bg-purple-700'" :class="adminUser?.is_super_admin ? 'bg-yellow-600 hover:bg-yellow-700' : 'bg-purple-600 hover:bg-purple-700'"
@@ -180,7 +180,7 @@
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-3">
<!-- Delete Admin User Button --> <!-- Delete Admin User Button -->
<button <button
@click="deleteAdminUser()" @click="showDeleteModal = true"
:disabled="saving || adminUser?.id === currentUserId" :disabled="saving || adminUser?.id === currentUserId"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50"
:title="adminUser?.id === currentUserId ? 'Cannot delete yourself' : 'Delete this admin user'"> :title="adminUser?.id === currentUserId ? 'Cannot delete yourself' : 'Delete this admin user'">
@@ -265,6 +265,54 @@
'Cancel', 'Cancel',
'warning' 'warning'
) }} ) }}
<!-- Toggle Super Admin Confirmation Modal -->
{{ confirm_modal_dynamic(
'toggleSuperAdminModal',
'Toggle Super Admin',
"'Are you sure you want to ' + (adminUser?.is_super_admin ? 'demote' : 'promote') + ' \"' + (adminUser?.username || '') + '\" ' + (adminUser?.is_super_admin ? 'from' : 'to') + ' super admin?'",
'toggleSuperAdmin()',
'showToggleSuperAdminModal',
'Confirm',
'Cancel',
'warning'
) }}
<!-- Toggle Status Confirmation Modal -->
{{ confirm_modal_dynamic(
'toggleStatusModal',
'Toggle User Status',
"'Are you sure you want to ' + (adminUser?.is_active ? 'deactivate' : 'activate') + ' \"' + (adminUser?.username || '') + '\"?'",
'toggleStatus()',
'showToggleStatusModal',
'Confirm',
'Cancel',
'warning'
) }}
<!-- Delete Admin User Confirmation Modal (Step 1) -->
{{ confirm_modal_dynamic(
'deleteAdminUserModal',
'Delete Admin User',
"'Are you sure you want to delete admin user \"' + (adminUser?.username || '') + '\"? This action cannot be undone.'",
'confirmDeleteStep()',
'showDeleteModal',
'Delete',
'Cancel',
'danger'
) }}
<!-- Delete Admin User Final Confirmation Modal (Step 2) -->
{{ confirm_modal_dynamic(
'deleteAdminUserFinalModal',
'Final Confirmation',
"'FINAL CONFIRMATION: Are you absolutely sure you want to permanently delete \"' + (adminUser?.username || '') + '\"?'",
'deleteAdminUser()',
'showDeleteFinalModal',
'Permanently Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -4,6 +4,7 @@
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/tables.html' import table_wrapper, table_header %} {% from 'shared/macros/tables.html' import table_wrapper, table_header %}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
{% block title %}Admin Users{% endblock %} {% block title %}Admin Users{% endblock %}
@@ -239,7 +240,7 @@
<!-- Delete Button (disabled for self) --> <!-- Delete Button (disabled for self) -->
<button <button
@click="deleteAdminUser(admin)" @click="adminToDelete = admin; showDeleteModal = true"
:disabled="admin.id === currentUserId" :disabled="admin.id === currentUserId"
class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
:title="admin.id === currentUserId ? 'Cannot delete yourself' : 'Delete admin user'" :title="admin.id === currentUserId ? 'Cannot delete yourself' : 'Delete admin user'"
@@ -255,6 +256,30 @@
{{ pagination() }} {{ pagination() }}
</div> </div>
<!-- Delete Admin User Confirmation Modal (Step 1) -->
{{ confirm_modal_dynamic(
'deleteAdminUserModal',
'Delete Admin User',
"'Are you sure you want to delete admin user \"' + (adminToDelete?.username || '') + '\"? This action cannot be undone.'",
'confirmDeleteStep()',
'showDeleteModal',
'Delete',
'Cancel',
'danger'
) }}
<!-- Delete Admin User Final Confirmation Modal (Step 2) -->
{{ confirm_modal_dynamic(
'deleteAdminUserFinalModal',
'Final Confirmation',
"'FINAL CONFIRMATION: Are you absolutely sure you want to permanently delete \"' + (adminToDelete?.username || '') + '\"?'",
'deleteAdminUser(adminToDelete)',
'showDeleteFinalModal',
'Permanently Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -4,6 +4,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/headers.html' import detail_page_header %} {% from 'shared/macros/headers.html' import detail_page_header %}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
{% block title %}Merchant Details{% endblock %} {% block title %}Merchant Details{% endblock %}
@@ -42,7 +43,7 @@
Create Subscription Create Subscription
</button> </button>
<button <button
@click="deleteMerchant()" @click="promptDeleteMerchant()"
:disabled="merchant?.store_count > 0" :disabled="merchant?.store_count > 0"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50 disabled:cursor-not-allowed" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50 disabled:cursor-not-allowed"
:title="merchant?.store_count > 0 ? 'Cannot delete merchant with stores' : 'Delete merchant'"> :title="merchant?.store_count > 0 ? 'Cannot delete merchant with stores' : 'Delete merchant'">
@@ -456,6 +457,28 @@
Stores created will be associated with this merchant. Stores created will be associated with this merchant.
</p> </p>
</div> </div>
{{ confirm_modal_dynamic(
'deleteMerchantModal',
'Delete Merchant',
"'Are you sure you want to delete merchant \"' + (merchant?.name || '') + '\"? This action cannot be undone.'",
'confirmDeleteMerchantStep()',
'showDeleteMerchantModal',
'Continue',
'Cancel',
'danger'
) }}
{{ confirm_modal_dynamic(
'deleteMerchantFinalModal',
'Final Confirmation',
"'Are you absolutely sure you want to delete \"' + (merchant?.name || '') + '\"?'",
'deleteMerchant()',
'showDeleteMerchantFinalModal',
'Delete Permanently',
'Cancel',
'danger'
) }}
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -3,6 +3,7 @@
{% from 'shared/macros/alerts.html' import loading_state %} {% from 'shared/macros/alerts.html' import loading_state %}
{% from 'shared/macros/inputs.html' import search_autocomplete, selected_item_display %} {% from 'shared/macros/inputs.html' import search_autocomplete, selected_item_display %}
{% 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 Merchant{% endblock %} {% block title %}Edit Merchant{% endblock %}
@@ -24,7 +25,7 @@
</h3> </h3>
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-3">
<button <button
@click="toggleVerification()" @click="showToggleVerificationModal = true"
:disabled="saving" :disabled="saving"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
:class="{ 'bg-orange-600 hover:bg-orange-700': merchant && merchant.is_verified, 'bg-green-600 hover:bg-green-700': merchant && !merchant.is_verified }"> :class="{ 'bg-orange-600 hover:bg-orange-700': merchant && merchant.is_verified, 'bg-green-600 hover:bg-green-700': merchant && !merchant.is_verified }">
@@ -33,7 +34,7 @@
</button> </button>
<button <button
@click="toggleActive()" @click="showToggleActiveModal = true"
:disabled="saving" :disabled="saving"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
:class="{ 'bg-red-600 hover:bg-red-700': merchant && merchant.is_active, 'bg-green-600 hover:bg-green-700': merchant && !merchant.is_active }"> :class="{ 'bg-red-600 hover:bg-red-700': merchant && merchant.is_active, 'bg-green-600 hover:bg-green-700': merchant && !merchant.is_active }">
@@ -290,7 +291,7 @@
<!-- Delete Merchant Button --> <!-- Delete Merchant Button -->
<button <button
@click="deleteMerchant()" @click="promptDeleteMerchant()"
:disabled="saving || (merchant?.store_count > 0)" :disabled="saving || (merchant?.store_count > 0)"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50"
:title="merchant?.store_count > 0 ? 'Cannot delete merchant with stores' : 'Delete this merchant'" :title="merchant?.store_count > 0 ? 'Cannot delete merchant with stores' : 'Delete this merchant'"
@@ -308,6 +309,50 @@
</p> </p>
</div> </div>
{{ confirm_modal_dynamic(
'toggleVerificationModal',
'Toggle Verification',
"merchant?.is_verified ? 'Are you sure you want to unverify this merchant?' : 'Are you sure you want to verify this merchant?'",
'toggleVerification()',
'showToggleVerificationModal',
'Confirm',
'Cancel',
'warning'
) }}
{{ confirm_modal_dynamic(
'toggleActiveModal',
'Toggle Active Status',
"merchant?.is_active ? 'Are you sure you want to deactivate this merchant? This will affect all stores under this merchant.' : 'Are you sure you want to activate this merchant? This will affect all stores under this merchant.'",
'toggleActive()',
'showToggleActiveModal',
'Confirm',
'Cancel',
'warning'
) }}
{{ confirm_modal_dynamic(
'deleteMerchantModal',
'Delete Merchant',
"'Are you sure you want to delete merchant \"' + (merchant?.name || '') + '\"? This action cannot be undone.'",
'confirmDeleteMerchantStep()',
'showDeleteMerchantModal',
'Continue',
'Cancel',
'danger'
) }}
{{ confirm_modal_dynamic(
'deleteMerchantFinalModal',
'Final Confirmation',
"'FINAL CONFIRMATION: Delete \"' + (merchant?.name || '') + '\"? This will permanently delete the merchant and all its data.'",
'deleteMerchant()',
'showDeleteMerchantFinalModal',
'Delete Permanently',
'Cancel',
'danger'
) }}
{# noqa: FE-004 - Complex form modal with dynamic user search and transfer functionality #} {# noqa: FE-004 - Complex form modal with dynamic user search and transfer functionality #}
<!-- Transfer Ownership Modal --> <!-- Transfer Ownership Modal -->
<div <div

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/headers.html' import detail_page_header %} {% from 'shared/macros/headers.html' import detail_page_header %}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
{% block title %}Merchant User Details{% endblock %} {% block title %}Merchant User Details{% endblock %}
@@ -27,7 +28,7 @@
</h3> </h3>
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-3">
<button <button
@click="toggleStatus()" @click="showToggleStatusModal = true"
:disabled="saving" :disabled="saving"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 border border-transparent rounded-lg focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 border border-transparent rounded-lg focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed"
:class="merchantUser?.is_active ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'"> :class="merchantUser?.is_active ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'">
@@ -35,7 +36,7 @@
<span x-text="merchantUser?.is_active ? 'Deactivate' : 'Activate'"></span> <span x-text="merchantUser?.is_active ? 'Deactivate' : 'Activate'"></span>
</button> </button>
<button <button
@click="deleteUser()" @click="showDeleteModal = true"
:disabled="saving" :disabled="saving"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50 disabled:cursor-not-allowed" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50 disabled:cursor-not-allowed"
title="Delete user"> title="Delete user">
@@ -243,6 +244,39 @@
</div> </div>
</div> </div>
</div> </div>
{{ confirm_modal_dynamic(
'toggleStatusModal',
'Toggle User Status',
"merchantUser?.is_active ? 'Are you sure you want to deactivate \"' + (merchantUser?.full_name || merchantUser?.username || '') + '\"?' : 'Are you sure you want to activate \"' + (merchantUser?.full_name || merchantUser?.username || '') + '\"?'",
'toggleStatus()',
'showToggleStatusModal',
'Confirm',
'Cancel',
'warning'
) }}
{{ confirm_modal_dynamic(
'deleteUserModal',
'Delete User',
"'Are you sure you want to delete \"' + (merchantUser?.full_name || merchantUser?.username || '') + '\"? This action cannot be undone.'",
'confirmDeleteStep()',
'showDeleteModal',
'Continue',
'Cancel',
'danger'
) }}
{{ confirm_modal_dynamic(
'deleteUserFinalModal',
'Final Confirmation',
"'Are you absolutely sure you want to delete \"' + (merchantUser?.full_name || merchantUser?.username || '') + '\"?'",
'deleteUser()',
'showDeleteFinalModal',
'Delete Permanently',
'Cancel',
'danger'
) }}
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -4,6 +4,7 @@
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/tables.html' import table_wrapper, table_header %} {% from 'shared/macros/tables.html' import table_wrapper, table_header %}
{% from 'shared/macros/modals.html' import confirm_modal, confirm_modal_dynamic %}
{% block title %}Merchant Users{% endblock %} {% block title %}Merchant Users{% endblock %}
@@ -198,7 +199,7 @@
<!-- Toggle Status Button --> <!-- Toggle Status Button -->
<button <button
@click="toggleUserStatus(user)" @click="userToToggle = user; showToggleStatusModal = true"
class="flex items-center justify-center p-2 rounded-lg hover:bg-orange-50 dark:hover:bg-gray-700 focus:outline-none transition-colors" class="flex items-center justify-center p-2 rounded-lg hover:bg-orange-50 dark:hover:bg-gray-700 focus:outline-none transition-colors"
:class="user.is_active ? 'text-orange-600 dark:text-orange-400' : 'text-green-600 dark:text-green-400'" :class="user.is_active ? 'text-orange-600 dark:text-orange-400' : 'text-green-600 dark:text-green-400'"
:title="user.is_active ? 'Deactivate user' : 'Activate user'" :title="user.is_active ? 'Deactivate user' : 'Activate user'"
@@ -208,7 +209,7 @@
<!-- Delete Button --> <!-- Delete Button -->
<button <button
@click="deleteUser(user)" @click="userToDelete = user; showDeleteModal = true"
class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors" class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors"
title="Delete user" title="Delete user"
> >
@@ -223,6 +224,42 @@
{{ pagination() }} {{ pagination() }}
</div> </div>
<!-- Toggle Status Confirmation Modal -->
{{ confirm_modal_dynamic(
'toggleStatusModal',
'Toggle User Status',
"'Are you sure you want to ' + (userToToggle?.is_active ? 'deactivate' : 'activate') + ' \"' + (userToToggle?.full_name || userToToggle?.username || userToToggle?.email || '') + '\"?'",
'toggleUserStatus(userToToggle)',
'showToggleStatusModal',
'Confirm',
'Cancel',
'warning'
) }}
<!-- Delete User Confirmation Modal (Step 1) -->
{{ confirm_modal_dynamic(
'deleteUserModal',
'Delete User',
"'Are you sure you want to delete \"' + (userToDelete?.full_name || userToDelete?.username || userToDelete?.email || '') + '\"? This action cannot be undone.'",
'confirmDeleteStep()',
'showDeleteModal',
'Delete',
'Cancel',
'danger'
) }}
<!-- Delete User Final Confirmation Modal (Step 2) -->
{{ confirm_modal_dynamic(
'deleteUserFinalModal',
'Final Confirmation',
"'FINAL CONFIRMATION: Are you absolutely sure you want to permanently delete \"' + (userToDelete?.full_name || userToDelete?.username || userToDelete?.email || '') + '\"?'",
'deleteUser(userToDelete)',
'showDeleteFinalModal',
'Permanently Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -4,6 +4,7 @@
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/tables.html' import table_wrapper, table_header %} {% from 'shared/macros/tables.html' import table_wrapper, table_header %}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
{% block title %}Merchants{% endblock %} {% block title %}Merchants{% endblock %}
@@ -225,7 +226,7 @@
<!-- Delete Button --> <!-- Delete Button -->
<button <button
@click="deleteMerchant(merchant)" @click="promptDeleteMerchant(merchant)"
class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors" class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors"
title="Delete merchant" title="Delete merchant"
:disabled="merchant.store_count > 0" :disabled="merchant.store_count > 0"
@@ -242,6 +243,17 @@
{{ pagination() }} {{ pagination() }}
</div> </div>
{{ confirm_modal_dynamic(
'deleteMerchantModal',
'Delete Merchant',
"'Are you sure you want to delete \"' + (merchantToDelete?.name || '') + '\"? This action cannot be undone.'",
'deleteMerchant(merchantToDelete)',
'showDeleteMerchantModal',
'Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import alert_dynamic, error_state %} {% from 'shared/macros/alerts.html' import alert_dynamic, error_state %}
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/modals.html' import confirm_modal %}
{% block title %}Module Configuration{% endblock %} {% block title %}Module Configuration{% endblock %}
@@ -121,7 +122,7 @@
<!-- Actions --> <!-- Actions -->
<div class="px-4 py-3 bg-gray-50 dark:bg-gray-700/50 border-t border-gray-200 dark:border-gray-700 flex items-center justify-between"> <div class="px-4 py-3 bg-gray-50 dark:bg-gray-700/50 border-t border-gray-200 dark:border-gray-700 flex items-center justify-between">
<button <button
@click="resetToDefaults()" @click="showResetConfirm = true"
:disabled="saving" :disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600 disabled:opacity-50" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600 disabled:opacity-50"
> >
@@ -141,6 +142,9 @@
</div> </div>
</div> </div>
<!-- Reset to Defaults Confirm Modal -->
{{ confirm_modal('resetConfirm', 'Reset Configuration', 'This will reset all configuration options to their default values. This action cannot be undone.', 'resetToDefaults()', 'showResetConfirm', 'Reset to Defaults', 'Cancel', 'warning') }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import alert_dynamic, error_state %} {% from 'shared/macros/alerts.html' import alert_dynamic, error_state %}
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/modals.html' import confirm_modal %}
{% block title %}Menu Configuration{% endblock %} {% block title %}Menu Configuration{% endblock %}
@@ -94,7 +95,7 @@
</p> </p>
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
@click="showAll()" @click="showShowAllModal = true"
:disabled="saving" :disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600"
> >
@@ -102,7 +103,7 @@
Show All Show All
</button> </button>
<button <button
@click="resetToDefaults()" @click="showHideAllModal = true"
:disabled="saving" :disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600"
> >
@@ -193,6 +194,30 @@
</div> </div>
</div> </div>
<!-- Show All Menu Items Confirmation Modal -->
{{ confirm_modal(
'showAllModal',
'Show All Menu Items',
'Are you sure you want to show all menu items? This will make all non-mandatory items visible.',
'showAll()',
'showShowAllModal',
'Show All',
'Cancel',
'info'
) }}
<!-- Hide All Menu Items Confirmation Modal -->
{{ confirm_modal(
'hideAllModal',
'Hide All Menu Items',
'Are you sure you want to hide all optional menu items? Only mandatory items will remain visible.',
'resetToDefaults()',
'showHideAllModal',
'Hide All',
'Cancel',
'warning'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import alert_dynamic, error_state %} {% from 'shared/macros/alerts.html' import alert_dynamic, error_state %}
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/modals.html' import confirm_modal %}
{% block title %}Module Configuration{% endblock %} {% block title %}Module Configuration{% endblock %}
@@ -76,7 +77,7 @@
</p> </p>
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
@click="enableAll()" @click="showEnableAllModal = true"
:disabled="saving" :disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600"
> >
@@ -84,7 +85,7 @@
Enable All Enable All
</button> </button>
<button <button
@click="disableOptional()" @click="showDisableOptionalModal = true"
:disabled="saving" :disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600"
> >
@@ -275,6 +276,30 @@
</div> </div>
</div> </div>
<!-- Enable All Modules Confirmation Modal -->
{{ confirm_modal(
'enableAllModal',
'Enable All Modules',
'Are you sure you want to enable all modules? This will activate all optional modules for this platform.',
'enableAll()',
'showEnableAllModal',
'Enable All',
'Cancel',
'info'
) }}
<!-- Disable Optional Modules Confirmation Modal -->
{{ confirm_modal(
'disableOptionalModal',
'Disable Optional Modules',
'Are you sure you want to disable all optional modules? Only core modules will remain enabled.',
'disableOptional()',
'showDisableOptionalModal',
'Disable Optional',
'Cancel',
'warning'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/headers.html' import detail_page_header %} {% from 'shared/macros/headers.html' import detail_page_header %}
{% from 'shared/macros/modals.html' import confirm_modal, confirm_modal_dynamic %}
{% block title %}Store Details{% endblock %} {% block title %}Store Details{% endblock %}
@@ -33,7 +34,7 @@
Edit Store Edit Store
</a> </a>
<button <button
@click="deleteStore()" @click="promptDeleteStore()"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red"> class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red">
<span x-html="$icon('delete', 'w-4 h-4 mr-2')"></span> <span x-html="$icon('delete', 'w-4 h-4 mr-2')"></span>
Delete Store Delete Store
@@ -385,6 +386,30 @@
Contact info and ownership are managed at the merchant level. Contact info and ownership are managed at the merchant level.
</p> </p>
</div> </div>
<!-- Delete Store Confirmation Modal (Step 1) -->
{{ confirm_modal_dynamic(
'deleteStoreModal',
'Delete Store',
"'Are you sure you want to delete \"' + (store?.name || '') + '\"? This will permanently delete all products, orders, customers, and team members. This action cannot be undone!'",
'confirmDeleteStoreStep()',
'showDeleteStoreModal',
'Delete',
'Cancel',
'danger'
) }}
<!-- Delete Store Final Confirmation Modal (Step 2) -->
{{ confirm_modal_dynamic(
'deleteStoreFinalModal',
'Final Confirmation',
"'FINAL CONFIRMATION: Are you absolutely sure you want to permanently delete \"' + (store?.name || '') + '\" and ALL associated data?'",
'deleteStore()',
'showDeleteStoreFinalModal',
'Permanently Delete',
'Cancel',
'danger'
) }}
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -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, confirm_modal_dynamic %}
{% block title %}Edit Store{% endblock %} {% block title %}Edit Store{% endblock %}
@@ -25,7 +26,7 @@
</h3> </h3>
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-3">
<button <button
@click="toggleVerification()" @click="showToggleVerificationModal = true"
:disabled="saving" :disabled="saving"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
:class="store?.is_verified ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'"> :class="store?.is_verified ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'">
@@ -34,7 +35,7 @@
</button> </button>
<button <button
@click="toggleActive()" @click="showToggleActiveModal = true"
:disabled="saving" :disabled="saving"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50" class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
:class="store?.is_active ? 'bg-red-600 hover:bg-red-700' : 'bg-green-600 hover:bg-green-700'"> :class="store?.is_active ? 'bg-red-600 hover:bg-red-700' : 'bg-green-600 hover:bg-green-700'">
@@ -425,6 +426,54 @@
</div> </div>
</form> </form>
</div> </div>
<!-- Toggle Verification Confirmation Modal -->
{{ confirm_modal_dynamic(
'toggleVerificationModal',
'Toggle Store Verification',
"store?.is_verified ? 'Are you sure you want to unverify this store?' : 'Are you sure you want to verify this store?'",
'toggleVerification()',
'showToggleVerificationModal',
'Confirm',
'Cancel',
'warning'
) }}
<!-- Toggle Active Status Confirmation Modal -->
{{ confirm_modal_dynamic(
'toggleActiveModal',
'Toggle Store Status',
"store?.is_active ? 'Are you sure you want to deactivate this store? This will affect their operations.' : 'Are you sure you want to activate this store?'",
'toggleActive()',
'showToggleActiveModal',
'Confirm',
'Cancel',
'warning'
) }}
<!-- Delete Store Confirmation Modal (Step 1) -->
{{ confirm_modal_dynamic(
'deleteStoreModal',
'Delete Store',
"'Are you sure you want to delete \"' + (store?.name || '') + '\"? This will permanently delete all products, orders, customers, and team members. This action cannot be undone!'",
'confirmDeleteStoreStep()',
'showDeleteStoreModal',
'Delete',
'Cancel',
'danger'
) }}
<!-- Delete Store Final Confirmation Modal (Step 2) -->
{{ confirm_modal_dynamic(
'deleteStoreFinalModal',
'Final Confirmation',
"'FINAL CONFIRMATION: Are you absolutely sure you want to permanently delete \"' + (store?.name || '') + '\" and ALL associated data?'",
'deleteStore()',
'showDeleteStoreFinalModal',
'Permanently Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -2,6 +2,7 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/headers.html' import page_header_flex %} {% from 'shared/macros/headers.html' import page_header_flex %}
{% from 'shared/macros/modals.html' import confirm_modal %}
{% block title %}Theme Editor - {{ store_code }}{% endblock %} {% block title %}Theme Editor - {{ store_code }}{% endblock %}
@@ -330,7 +331,7 @@
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<button @click="resetToDefault()" <button @click="showResetThemeModal = true"
:disabled="saving" :disabled="saving"
class="px-4 py-2 text-sm font-medium leading-5 text-red-700 transition-colors duration-150 bg-white border border-red-300 rounded-lg hover:bg-red-50 focus:outline-none disabled:opacity-50 dark:bg-gray-800 dark:text-red-400 dark:border-red-600"> class="px-4 py-2 text-sm font-medium leading-5 text-red-700 transition-colors duration-150 bg-white border border-red-300 rounded-lg hover:bg-red-50 focus:outline-none disabled:opacity-50 dark:bg-gray-800 dark:text-red-400 dark:border-red-600">
<span x-html="$icon('refresh', 'inline w-4 h-4 mr-2')"></span> <span x-html="$icon('refresh', 'inline w-4 h-4 mr-2')"></span>
@@ -443,6 +444,18 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Reset Theme Confirmation Modal -->
{{ confirm_modal(
'resetThemeModal',
'Reset Theme',
'Are you sure you want to reset the theme to default? All customizations will be lost.',
'resetTheme()',
'showResetThemeModal',
'Reset',
'Cancel',
'warning'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -4,6 +4,7 @@
{% from 'shared/macros/headers.html' import page_header %} {% from 'shared/macros/headers.html' import page_header %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %} {% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/tables.html' import table_wrapper, table_header %} {% from 'shared/macros/tables.html' import table_wrapper, table_header %}
{% from 'shared/macros/modals.html' import confirm_modal, confirm_modal_dynamic %}
{% block title %}Stores{% endblock %} {% block title %}Stores{% endblock %}
@@ -210,7 +211,7 @@
<!-- Delete Button --> <!-- Delete Button -->
<button <button
@click="deleteStore(store)" @click="promptDeleteStore(store)"
class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors" class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors"
title="Delete store" title="Delete store"
> >
@@ -225,6 +226,18 @@
{{ pagination() }} {{ pagination() }}
</div> </div>
<!-- Delete Store Confirmation Modal -->
{{ confirm_modal_dynamic(
'deleteStoreModal',
'Delete Store',
"'Are you sure you want to delete \"' + (storeToDelete?.name || '') + '\"? This action cannot be undone.'",
'deleteStore(storeToDelete)',
'showDeleteStoreModal',
'Delete',
'Cancel',
'danger'
) }}
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}

View File

@@ -1,4 +1,5 @@
{# app/templates/store/base.html #} {# app/templates/store/base.html #}
{% from 'shared/macros/modals.html' import confirm_modal_dynamic %}
<!DOCTYPE html> <!DOCTYPE html>
<html :class="{ 'dark': dark }" x-data="{% block alpine_data %}data(){% endblock %}" lang="en"> <html :class="{ 'dark': dark }" x-data="{% block alpine_data %}data(){% endblock %}" lang="en">
<head> <head>
@@ -45,6 +46,9 @@
</div> </div>
</div> </div>
<!-- Upgrade Limit Reached Confirm Modal -->
{{ confirm_modal_dynamic('limitReachedConfirm', 'Usage Limit Reached', '$store.upgrade.limitReachedMessage', '$store.upgrade.confirmUpgrade()', '$store.upgrade.showLimitReachedConfirm', 'Go to Billing', 'Dismiss', 'warning') }}
<!-- Core Scripts - ORDER MATTERS! --> <!-- Core Scripts - ORDER MATTERS! -->
<!-- 1. FIRST: Log Configuration --> <!-- 1. FIRST: Log Configuration -->

View File

@@ -17,6 +17,9 @@ function adminModuleConfig(platformCode, moduleCode) {
successMessage: null, successMessage: null,
saving: false, saving: false,
// Reset confirm state
showResetConfirm: false,
// Data // Data
platformId: null, platformId: null,
platformName: '', platformName: '',
@@ -117,10 +120,6 @@ function adminModuleConfig(platformCode, moduleCode) {
}, },
async resetToDefaults() { async resetToDefaults() {
if (!confirm('This will reset all configuration options to their default values. Continue?')) {
return;
}
this.saving = true; this.saving = true;
this.error = null; this.error = null;
this.successMessage = null; this.successMessage = null;