snapshot of https://github.com/seaweedfs/seaweedfs.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-16 03:23:42 +05:00
commit 89f5be005d
3997 changed files with 961970 additions and 0 deletions

View File

@@ -0,0 +1,338 @@
/* SeaweedFS Dashboard Custom Styles */
:root {
--navbar-height: 56px;
}
/* Link colors - muted */
a {
color: #5b7c99;
}
a:hover {
color: #4a6a88;
}
/* Sidebar Styles */
.sidebar {
position: fixed;
top: var(--navbar-height);
bottom: 0;
left: 0;
z-index: 100;
padding: 48px 0 0;
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
overflow-y: auto;
}
.sidebar-heading {
font-size: .75rem;
text-transform: uppercase;
}
.sidebar .nav-link {
font-weight: 500;
color: #333;
}
.sidebar .nav-link:hover {
color: #5b7c99;
}
.sidebar .nav-link.active {
color: #5b7c99;
}
.sidebar .nav-link:hover .feather,
.sidebar .nav-link.active .feather {
color: inherit;
}
/* Main content area */
main {
margin-left: 240px;
}
@media (max-width: 767.98px) {
.sidebar {
top: var(--navbar-height);
padding-top: 0;
width: 240px;
background-color: #f8f9fa !important;
z-index: 1050;
}
.sidebar.show ~ .sidebar-backdrop {
display: block;
}
main {
margin-left: 0;
}
}
/* Sidebar backdrop for mobile overlay */
.sidebar-backdrop {
display: none;
position: fixed;
top: var(--navbar-height);
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1040;
}
/* Custom card styles */
.border-left-primary {
border-left: 0.25rem solid #6b8caf !important;
}
.border-left-success {
border-left: 0.25rem solid #5a8a72 !important;
}
.border-left-info {
border-left: 0.25rem solid #6a9aaa !important;
}
.border-left-warning {
border-left: 0.25rem solid #b8995e !important;
}
.border-left-danger {
border-left: 0.25rem solid #a5615c !important;
}
/* Status badges */
.badge {
font-size: 0.875em;
}
/* Muted badge colors - override Bootstrap defaults */
.badge.bg-primary,
.bg-primary {
background-color: #6b8caf !important;
}
.badge.bg-success,
.bg-success {
background-color: #5a8a72 !important;
}
.badge.bg-info,
.bg-info {
background-color: #6a9aaa !important;
}
.badge.bg-warning,
.bg-warning {
background-color: #b8995e !important;
}
.badge.bg-danger,
.bg-danger {
background-color: #a5615c !important;
}
.badge.bg-secondary,
.bg-secondary {
background-color: #7a7d85 !important;
}
/* Muted card background colors for text-bg-* utility classes */
.text-bg-primary,
.card.text-bg-primary {
background-color: #6b8caf !important;
color: #fff !important;
}
.text-bg-success,
.card.text-bg-success {
background-color: #5a8a72 !important;
color: #fff !important;
}
.text-bg-info,
.card.text-bg-info {
background-color: #6a9aaa !important;
color: #fff !important;
}
.text-bg-warning,
.card.text-bg-warning {
background-color: #b8995e !important;
color: #fff !important;
}
.text-bg-danger,
.card.text-bg-danger {
background-color: #a5615c !important;
color: #fff !important;
}
/* Muted text color utilities */
.text-primary {
color: #6b8caf !important;
}
.text-success {
color: #5a8a72 !important;
}
.text-info {
color: #6a9aaa !important;
}
.text-warning {
color: #b8995e !important;
}
.text-danger {
color: #a5615c !important;
}
/* Progress bars */
.progress {
background-color: #f8f9fc;
border: 1px solid #e3e6f0;
}
.progress-bar {
font-size: 0.75rem;
font-weight: 700;
color: #fff;
text-align: center;
}
/* Tables */
.table {
color: #5a5c69;
}
.table thead th {
vertical-align: bottom;
border-bottom: 1px solid #e3e6f0;
font-weight: 700;
color: #5a5c69;
background-color: #f8f9fc;
}
.table-bordered {
border: 1px solid #e3e6f0;
}
.table-bordered th,
.table-bordered td {
border: 1px solid #e3e6f0;
}
/* Cards */
.card {
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15) !important;
border: 1px solid #e3e6f0;
}
.card-header {
background-color: #f8f9fc;
border-bottom: 1px solid #e3e6f0;
}
/* Buttons */
.btn-primary {
background-color: #6b8caf;
border-color: #6b8caf;
}
.btn-primary:hover {
background-color: #5b7c99;
border-color: #5b7c99;
}
/* Text utilities */
.text-gray-800 {
color: #5a5c69 !important;
}
.text-gray-300 {
color: #dddfeb !important;
}
/* Animation for HTMX updates */
.htmx-indicator {
opacity: 0;
transition: opacity 500ms ease-in;
}
.htmx-request .htmx-indicator {
opacity: 1;
}
.htmx-request.htmx-indicator {
opacity: 1;
}
/* Loading spinner */
.spinner-border-sm {
width: 1rem;
height: 1rem;
}
/* Custom utilities */
.bg-gradient-primary {
background: linear-gradient(180deg, #6b8caf 10%, #5b7c99 100%);
}
.shadow {
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15) !important;
}
/* Navbar user icon color */
.navbar-toggler .fa-user {
color: rgba(255, 255, 255, 0.75);
}
/* Collapsible menu styles */
.nav-link[data-bs-toggle="collapse"] {
position: relative;
}
.nav-link[data-bs-toggle="collapse"] .fa-chevron-down {
transition: transform 0.2s ease;
}
.nav-link[data-bs-toggle="collapse"][aria-expanded="true"] .fa-chevron-down {
transform: rotate(180deg);
}
.nav-link[data-bs-toggle="collapse"]:not(.collapsed) {
color: #5b7c99;
}
.nav-link[data-bs-toggle="collapse"]:not(.collapsed) .fa-chevron-down {
color: #5b7c99;
}
/* Submenu styles */
.nav .nav {
border-left: 1px solid #e3e6f0;
margin-left: 0.5rem;
}
.nav .nav .nav-link {
font-size: 0.875rem;
padding-left: 1rem;
}
.nav .nav .nav-link:hover {
background-color: #f8f9fc;
}
/* Responsive adjustments */
@media (max-width: 576px) {
.card-body {
padding: 1rem;
}
.h5 {
font-size: 1rem;
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

1
weed/admin/static/js/htmx.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,83 @@
/**
* Shared IAM utility functions for the SeaweedFS Admin Dashboard.
*/
// URL prefix helper for subdirectory deployment
function iamBasePath(path) {
return (window.__BASE_PATH__ || '') + path;
}
// Delete user function
async function deleteUser(username) {
showDeleteConfirm(username, async function () {
try {
const encodedUsername = encodeURIComponent(username);
const response = await fetch(iamBasePath(`/api/users/${encodedUsername}`), {
method: 'DELETE'
});
if (response.ok) {
showAlert('User deleted successfully', 'success');
setTimeout(() => window.location.reload(), 1000);
} else {
const error = await response.json().catch(() => ({}));
showAlert('Failed to delete user: ' + (error.error || 'Unknown error'), 'error');
}
} catch (error) {
console.error('Error deleting user:', error);
showAlert('Failed to delete user: ' + error.message, 'error');
}
}, 'Are you sure you want to delete this user? This action cannot be undone.');
}
// Delete group function
async function deleteGroup(name) {
showDeleteConfirm(name, async function () {
try {
const encodedName = encodeURIComponent(name);
const response = await fetch(iamBasePath(`/api/groups/${encodedName}`), {
method: 'DELETE'
});
if (response.ok) {
showAlert('Group deleted successfully', 'success');
setTimeout(() => window.location.reload(), 1000);
} else {
const error = await response.json().catch(() => ({}));
showAlert('Failed to delete group: ' + (error.error || 'Unknown error'), 'error');
}
} catch (error) {
console.error('Error deleting group:', error);
showAlert('Failed to delete group: ' + error.message, 'error');
}
}, 'Are you sure you want to delete this group? This action cannot be undone.');
}
// Delete access key function
async function deleteAccessKey(username, accessKey) {
showDeleteConfirm(accessKey, async function () {
try {
const encodedUsername = encodeURIComponent(username);
const encodedAccessKey = encodeURIComponent(accessKey);
const response = await fetch(iamBasePath(`/api/users/${encodedUsername}/access-keys/${encodedAccessKey}`), {
method: 'DELETE'
});
if (response.ok) {
showAlert('Access key deleted successfully', 'success');
// If refreshAccessKeysList exists (in object_store_users.templ), use it
if (typeof refreshAccessKeysList === 'function') {
refreshAccessKeysList(username);
} else {
setTimeout(() => window.location.reload(), 1000);
}
} else {
const error = await response.json().catch(() => ({}));
showAlert('Failed to delete access key: ' + (error.error || 'Unknown error'), 'error');
}
} catch (error) {
console.error('Error deleting access key:', error);
showAlert('Failed to delete access key: ' + error.message, 'error');
}
}, 'Are you sure you want to delete this access key?');
}

View File

@@ -0,0 +1,394 @@
/**
* 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><span id="globalConfirmModalTitleText">Confirm Action</span>
</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|object} typeOrOptions - Type ('success', 'error', 'warning', 'info') or options object
* @param {string} title - Optional custom title
*/
window.showAlert = function (message, typeOrOptions, title) {
ensureModalsExist();
let type = 'info';
let isHtml = false;
if (typeof typeOrOptions === 'object' && typeOrOptions !== null) {
type = typeOrOptions.type || 'info';
isHtml = typeOrOptions.isHtml || false;
title = typeOrOptions.title || title;
} else if (typeof typeOrOptions === 'string') {
type = typeOrOptions;
}
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 (isHtml || message.includes('<p>') || message.includes('<ul>')) {
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 transient message that does not have to be dismissed
* @param {string} message - The message to display
* @param {string} type - Bootstrap contextual color, defaults to 'success'
*/
window.showToast = function (message, type) {
let container = document.getElementById('globalToastContainer');
if (!container) {
container = document.createElement('div');
container.id = 'globalToastContainer';
container.className = 'toast-container position-fixed top-0 end-0 p-3';
// Above any modal, however deeply they are stacked.
container.style.zIndex = '9999';
document.body.appendChild(container);
}
const toast = document.createElement('div');
toast.className = 'toast align-items-center text-white bg-' + (type || 'success') + ' border-0';
toast.setAttribute('role', 'alert');
toast.innerHTML = '<div class="d-flex"><div class="toast-body">' + escapeHtml(message) + '</div>' +
'<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button></div>';
container.appendChild(toast);
toast.addEventListener('hidden.bs.toast', function () {
toast.remove();
});
new bootstrap.Toast(toast, { delay: 3000 }).show();
};
/**
* Show a confirmation dialog using Bootstrap modal
* @param {string} message - The confirmation message
* @param {function} onConfirm - Callback function if user confirms
* @param {function|object} onCancelOrOptions - Optional callback or options object
* @param {string} title - Optional custom title
*/
window.showConfirm = function (message, onConfirm, onCancelOrOptions, title) {
ensureModalsExist();
let onCancel = null;
let isHtml = false;
if (typeof onCancelOrOptions === 'object' && onCancelOrOptions !== null) {
onCancel = onCancelOrOptions.onCancel;
isHtml = onCancelOrOptions.isHtml || false;
title = onCancelOrOptions.title || null;
} else {
onCancel = onCancelOrOptions;
}
const modalEl = document.getElementById('globalConfirmModal');
const bodyEl = document.getElementById('globalConfirmModalBody');
const titleEl = document.getElementById('globalConfirmModalTitleText');
const okBtn = document.getElementById('globalConfirmOkBtn');
const cancelBtn = document.getElementById('globalConfirmCancelBtn');
// Set title
if (title) {
titleEl.textContent = title;
} else {
titleEl.textContent = 'Confirm Action';
}
// Set message
if (isHtml || message.includes('<p>') || message.includes('<ul>')) {
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 = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, function (m) { return map[m]; });
}
// Bootstrap gives every modal and backdrop the same z-index, so a modal
// opened while another one is showing paints behind it and cannot be
// clicked. Lift each nested modal, and its backdrop, above what is already
// on screen.
const MODAL_Z_INDEX = 1055;
const MODAL_Z_INDEX_STEP = 20;
document.addEventListener('show.bs.modal', function (event) {
const depth = document.querySelectorAll('.modal.show').length;
if (depth === 0) {
return;
}
const zIndex = MODAL_Z_INDEX + depth * MODAL_Z_INDEX_STEP;
event.target.style.zIndex = zIndex;
// Bootstrap only creates the backdrop after this event returns.
setTimeout(function () {
const backdrops = document.querySelectorAll('.modal-backdrop');
const backdrop = backdrops[backdrops.length - 1];
if (backdrop) {
backdrop.style.zIndex = zIndex - 1;
}
}, 0);
});
document.addEventListener('hidden.bs.modal', function (event) {
event.target.style.zIndex = '';
// Closing any modal releases the scroll lock the others still need.
if (document.querySelector('.modal.show')) {
document.body.classList.add('modal-open');
}
});
// 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 msg = String(message || '');
const msgLower = msg.toLowerCase();
// Refined type inference to avoid false positives
if (msgLower.includes('success') || msgLower.includes('successfully') || msgLower.includes('created') || msgLower.includes('updated') || msgLower.includes('saved')) {
// Avoid "not successful"
if (!msgLower.includes('not success')) {
type = 'success';
}
}
if (type === 'info') {
if (msgLower.includes('error') || msgLower.includes('failed') || msgLower.includes('invalid') || msgLower.includes('exception')) {
type = 'error';
} else if (msgLower.includes('warning') || msgLower.includes('required') || msgLower.includes('attention')) {
type = 'warning';
}
}
showAlert(msg, type);
};
console.log('Modal Alerts library loaded - native alert() overridden');
})();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.