31 changed files with 353 additions and 49 deletions
-
302weed/admin/static/js/modal-alerts.js
-
4weed/admin/view/app/cluster_ec_shards.templ
-
2weed/admin/view/app/cluster_ec_shards_templ.go
-
4weed/admin/view/app/cluster_ec_volumes.templ
-
2weed/admin/view/app/cluster_ec_volumes_templ.go
-
4weed/admin/view/app/collection_details.templ
-
2weed/admin/view/app/collection_details_templ.go
-
4weed/admin/view/app/file_browser.templ
-
2weed/admin/view/app/file_browser_templ.go
-
4weed/admin/view/app/maintenance_config.templ
-
4weed/admin/view/app/maintenance_config_schema.templ
-
2weed/admin/view/app/maintenance_config_schema_templ.go
-
2weed/admin/view/app/maintenance_config_templ.go
-
4weed/admin/view/app/maintenance_workers.templ
-
2weed/admin/view/app/maintenance_workers_templ.go
-
8weed/admin/view/app/object_store_users.templ
-
2weed/admin/view/app/object_store_users_templ.go
-
6weed/admin/view/app/policies.templ
-
2weed/admin/view/app/policies_templ.go
-
4weed/admin/view/app/service_accounts.templ
-
2weed/admin/view/app/service_accounts_templ.go
-
4weed/admin/view/app/task_config.templ
-
4weed/admin/view/app/task_config_schema.templ
-
2weed/admin/view/app/task_config_schema_templ.go
-
2weed/admin/view/app/task_config_templ.go
-
4weed/admin/view/app/task_config_templ.templ
-
2weed/admin/view/app/task_config_templ_templ.go
-
4weed/admin/view/app/task_detail.templ
-
2weed/admin/view/app/task_detail_templ.go
-
2weed/admin/view/layout/layout.templ
-
8weed/admin/view/layout/layout_templ.go
@ -0,0 +1,302 @@ |
|||||
|
/** |
||||
|
* Modal Alerts - Bootstrap Modal replacement for native alert() and confirm() |
||||
|
* Fixes Chrome auto-dismiss issue with native dialogs |
||||
|
* |
||||
|
* Usage: |
||||
|
* showAlert('Message', 'success'); |
||||
|
* showConfirm('Delete this?', function() { }); |
||||
|
*/ |
||||
|
|
||||
|
(function() { |
||||
|
'use strict'; |
||||
|
|
||||
|
// Create and inject modal HTML into page if not already present
|
||||
|
function ensureModalsExist() { |
||||
|
if (document.getElementById('globalAlertModal')) { |
||||
|
return; // Already exists
|
||||
|
} |
||||
|
|
||||
|
const modalsHTML = `
|
||||
|
<!-- Global Alert Modal --> |
||||
|
<div class="modal fade" id="globalAlertModal" tabindex="-1" aria-labelledby="globalAlertModalLabel" aria-hidden="true"> |
||||
|
<div class="modal-dialog"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header" id="globalAlertModalHeader"> |
||||
|
<h5 class="modal-title" id="globalAlertModalLabel"> |
||||
|
<i class="fas fa-info-circle me-2" id="globalAlertModalIcon"></i> |
||||
|
<span id="globalAlertModalTitle">Notice</span> |
||||
|
</h5> |
||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
||||
|
</div> |
||||
|
<div class="modal-body" id="globalAlertModalBody"> |
||||
|
<!-- Message will be inserted here --> |
||||
|
</div> |
||||
|
<div class="modal-footer"> |
||||
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">OK</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Global Confirm Modal --> |
||||
|
<div class="modal fade" id="globalConfirmModal" tabindex="-1" aria-labelledby="globalConfirmModalLabel" aria-hidden="true"> |
||||
|
<div class="modal-dialog"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header bg-warning"> |
||||
|
<h5 class="modal-title" id="globalConfirmModalLabel"> |
||||
|
<i class="fas fa-question-circle me-2"></i>Confirm Action |
||||
|
</h5> |
||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
||||
|
</div> |
||||
|
<div class="modal-body" id="globalConfirmModalBody"> |
||||
|
<!-- Message will be inserted here --> |
||||
|
</div> |
||||
|
<div class="modal-footer"> |
||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="globalConfirmCancelBtn">Cancel</button> |
||||
|
<button type="button" class="btn btn-primary" id="globalConfirmOkBtn">OK</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Global Delete Confirm Modal --> |
||||
|
<div class="modal fade" id="globalDeleteModal" tabindex="-1" aria-labelledby="globalDeleteModalLabel" aria-hidden="true"> |
||||
|
<div class="modal-dialog"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header bg-danger text-white"> |
||||
|
<h5 class="modal-title" id="globalDeleteModalLabel"> |
||||
|
<i class="fas fa-exclamation-triangle me-2"></i>Confirm Delete |
||||
|
</h5> |
||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> |
||||
|
</div> |
||||
|
<div class="modal-body"> |
||||
|
<p class="mb-2" id="globalDeleteModalMessage">Are you sure you want to delete this item?</p> |
||||
|
<p class="mb-0"><strong id="globalDeleteModalItemName"></strong></p> |
||||
|
<p class="text-muted small mt-2 mb-0">This action cannot be undone.</p> |
||||
|
</div> |
||||
|
<div class="modal-footer"> |
||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> |
||||
|
<button type="button" class="btn btn-danger" id="globalDeleteConfirmBtn"> |
||||
|
<i class="fas fa-trash me-1"></i>Delete |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
`;
|
||||
|
|
||||
|
// Inject modals at end of body
|
||||
|
document.body.insertAdjacentHTML('beforeend', modalsHTML); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Show an alert message using Bootstrap modal |
||||
|
* @param {string} message - The message to display |
||||
|
* @param {string} type - Type: 'success', 'error', 'warning', 'info' (default: 'info') |
||||
|
* @param {string} title - Optional custom title |
||||
|
*/ |
||||
|
window.showAlert = function(message, type, title) { |
||||
|
ensureModalsExist(); |
||||
|
|
||||
|
const modal = document.getElementById('globalAlertModal'); |
||||
|
const header = document.getElementById('globalAlertModalHeader'); |
||||
|
const titleEl = document.getElementById('globalAlertModalTitle'); |
||||
|
const bodyEl = document.getElementById('globalAlertModalBody'); |
||||
|
const iconEl = document.getElementById('globalAlertModalIcon'); |
||||
|
|
||||
|
// Configuration for different types
|
||||
|
const types = { |
||||
|
'success': { |
||||
|
title: 'Success', |
||||
|
icon: 'fa-check-circle', |
||||
|
headerClass: 'bg-success text-white', |
||||
|
btnClose: 'btn-close-white' |
||||
|
}, |
||||
|
'error': { |
||||
|
title: 'Error', |
||||
|
icon: 'fa-exclamation-triangle', |
||||
|
headerClass: 'bg-danger text-white', |
||||
|
btnClose: 'btn-close-white' |
||||
|
}, |
||||
|
'warning': { |
||||
|
title: 'Warning', |
||||
|
icon: 'fa-exclamation-circle', |
||||
|
headerClass: 'bg-warning text-dark', |
||||
|
btnClose: '' |
||||
|
}, |
||||
|
'info': { |
||||
|
title: 'Notice', |
||||
|
icon: 'fa-info-circle', |
||||
|
headerClass: 'bg-info text-white', |
||||
|
btnClose: 'btn-close-white' |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const config = types[type] || types['info']; |
||||
|
|
||||
|
// Update header styling
|
||||
|
header.className = 'modal-header ' + config.headerClass; |
||||
|
const closeBtn = header.querySelector('.btn-close'); |
||||
|
closeBtn.className = 'btn-close ' + config.btnClose; |
||||
|
|
||||
|
// Update icon
|
||||
|
iconEl.className = 'fas ' + config.icon + ' me-2'; |
||||
|
|
||||
|
// Update title
|
||||
|
titleEl.textContent = title || config.title; |
||||
|
|
||||
|
// Update body - support HTML or text
|
||||
|
if (message.includes('<')) { |
||||
|
bodyEl.innerHTML = message; |
||||
|
} else { |
||||
|
bodyEl.innerHTML = '<p class="mb-0">' + escapeHtml(message) + '</p>'; |
||||
|
} |
||||
|
|
||||
|
// Show modal
|
||||
|
const bsModal = new bootstrap.Modal(modal); |
||||
|
bsModal.show(); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Show a confirmation dialog using Bootstrap modal |
||||
|
* @param {string} message - The confirmation message |
||||
|
* @param {function} onConfirm - Callback function if user confirms |
||||
|
* @param {function} onCancel - Optional callback function if user cancels |
||||
|
* @param {string} title - Optional custom title |
||||
|
*/ |
||||
|
window.showConfirm = function(message, onConfirm, onCancel, title) { |
||||
|
ensureModalsExist(); |
||||
|
|
||||
|
const modalEl = document.getElementById('globalConfirmModal'); |
||||
|
const bodyEl = document.getElementById('globalConfirmModalBody'); |
||||
|
const titleEl = document.getElementById('globalConfirmModalLabel').querySelector('span'); |
||||
|
const okBtn = document.getElementById('globalConfirmOkBtn'); |
||||
|
const cancelBtn = document.getElementById('globalConfirmCancelBtn'); |
||||
|
|
||||
|
// Set title if provided
|
||||
|
if (title) { |
||||
|
if (titleEl) { |
||||
|
titleEl.textContent = title; |
||||
|
} else { |
||||
|
document.getElementById('globalConfirmModalLabel').insertAdjacentHTML('beforeend', '<span>' + escapeHtml(title) + '</span>'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Set message
|
||||
|
if (message.includes('<')) { |
||||
|
bodyEl.innerHTML = message; |
||||
|
} else { |
||||
|
bodyEl.innerHTML = '<p class="mb-0">' + escapeHtml(message) + '</p>'; |
||||
|
} |
||||
|
|
||||
|
// Remove old event listeners by cloning buttons
|
||||
|
const newOkBtn = okBtn.cloneNode(true); |
||||
|
const newCancelBtn = cancelBtn.cloneNode(true); |
||||
|
okBtn.parentNode.replaceChild(newOkBtn, okBtn); |
||||
|
cancelBtn.parentNode.replaceChild(newCancelBtn, cancelBtn); |
||||
|
|
||||
|
const modal = new bootstrap.Modal(modalEl); |
||||
|
|
||||
|
// Add event listeners
|
||||
|
newOkBtn.addEventListener('click', function() { |
||||
|
modal.hide(); |
||||
|
if (typeof onConfirm === 'function') { |
||||
|
onConfirm(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
newCancelBtn.addEventListener('click', function() { |
||||
|
modal.hide(); |
||||
|
if (typeof onCancel === 'function') { |
||||
|
onCancel(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
modal.show(); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Show a delete confirmation dialog |
||||
|
* @param {string} itemName - Name of the item to delete |
||||
|
* @param {function} onConfirm - Callback function if user confirms deletion |
||||
|
* @param {string} message - Optional custom message (default: "Are you sure you want to delete this item?") |
||||
|
*/ |
||||
|
window.showDeleteConfirm = function(itemName, onConfirm, message) { |
||||
|
ensureModalsExist(); |
||||
|
|
||||
|
const modalEl = document.getElementById('globalDeleteModal'); |
||||
|
const messageEl = document.getElementById('globalDeleteModalMessage'); |
||||
|
const itemNameEl = document.getElementById('globalDeleteModalItemName'); |
||||
|
const confirmBtn = document.getElementById('globalDeleteConfirmBtn'); |
||||
|
|
||||
|
// Set custom message if provided
|
||||
|
if (message) { |
||||
|
messageEl.textContent = message; |
||||
|
} else { |
||||
|
messageEl.textContent = 'Are you sure you want to delete this item?'; |
||||
|
} |
||||
|
|
||||
|
// Set item name
|
||||
|
itemNameEl.textContent = itemName; |
||||
|
|
||||
|
// Remove old event listener by cloning button
|
||||
|
const newConfirmBtn = confirmBtn.cloneNode(true); |
||||
|
confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn); |
||||
|
|
||||
|
const modal = new bootstrap.Modal(modalEl); |
||||
|
|
||||
|
// Add new event listener
|
||||
|
newConfirmBtn.addEventListener('click', function() { |
||||
|
modal.hide(); |
||||
|
if (typeof onConfirm === 'function') { |
||||
|
onConfirm(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
modal.show(); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Escape HTML to prevent XSS |
||||
|
*/ |
||||
|
function escapeHtml(text) { |
||||
|
const map = { |
||||
|
'&': '&', |
||||
|
'<': '<', |
||||
|
'>': '>', |
||||
|
'"': '"', |
||||
|
"'": ''' |
||||
|
}; |
||||
|
return text.replace(/[&<>"']/g, function(m) { return map[m]; }); |
||||
|
} |
||||
|
|
||||
|
// Auto-initialize on DOMContentLoaded
|
||||
|
if (document.readyState === 'loading') { |
||||
|
document.addEventListener('DOMContentLoaded', ensureModalsExist); |
||||
|
} else { |
||||
|
ensureModalsExist(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* AUTOMATIC OVERRIDE of native alert() |
||||
|
* This makes ALL existing alert() calls automatically use Bootstrap modals |
||||
|
*/ |
||||
|
window.alert = function(message) { |
||||
|
// Auto-detect message type from content
|
||||
|
let type = 'info'; |
||||
|
const msgLower = (message || '').toLowerCase(); |
||||
|
|
||||
|
if (msgLower.includes('success') || msgLower.includes('created') || msgLower.includes('updated') || msgLower.includes('saved')) { |
||||
|
type = 'success'; |
||||
|
} else if (msgLower.includes('error') || msgLower.includes('failed') || msgLower.includes('invalid') || msgLower.includes('cannot')) { |
||||
|
type = 'error'; |
||||
|
} else if (msgLower.includes('warning') || msgLower.includes('please') || msgLower.includes('required')) { |
||||
|
type = 'warning'; |
||||
|
} |
||||
|
|
||||
|
showAlert(message, type); |
||||
|
}; |
||||
|
|
||||
|
console.log('Modal Alerts library loaded - native alert() overridden'); |
||||
|
console.log('For confirm(), use showConfirm() or showDeleteConfirm() instead of native confirm()'); |
||||
|
})(); |
||||
2
weed/admin/view/app/file_browser_templ.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
weed/admin/view/app/maintenance_config_schema_templ.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
weed/admin/view/app/maintenance_workers_templ.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
weed/admin/view/app/object_store_users_templ.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
weed/admin/view/app/policies_templ.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
weed/admin/view/app/service_accounts_templ.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
weed/admin/view/app/task_detail_templ.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue