progress
This commit is contained in:
@@ -20,17 +20,7 @@ async function initializeApp() {
|
||||
window.keyboardShortcuts.initialize();
|
||||
}
|
||||
|
||||
// Initialize tooltips
|
||||
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
});
|
||||
|
||||
// Initialize popovers
|
||||
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
|
||||
popoverTriggerList.map(function (popoverTriggerEl) {
|
||||
return new bootstrap.Popover(popoverTriggerEl);
|
||||
});
|
||||
// Remove Bootstrap-dependent tooltips/popovers; use native title/tooltips if needed
|
||||
|
||||
// Add form validation classes
|
||||
initializeFormValidation();
|
||||
@@ -44,19 +34,19 @@ async function initializeApp() {
|
||||
|
||||
// Form validation
|
||||
function initializeFormValidation() {
|
||||
// Add Bootstrap validation styles
|
||||
const forms = document.querySelectorAll('form.needs-validation');
|
||||
// Native validation handling without Bootstrap classes
|
||||
const forms = document.querySelectorAll('form');
|
||||
forms.forEach(form => {
|
||||
form.addEventListener('submit', function(event) {
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
form.reportValidity();
|
||||
}
|
||||
form.classList.add('was-validated');
|
||||
});
|
||||
});
|
||||
|
||||
// Real-time validation for specific fields
|
||||
// Real-time validation for required fields (Tailwind styles)
|
||||
const requiredFields = document.querySelectorAll('input[required], select[required], textarea[required]');
|
||||
requiredFields.forEach(field => {
|
||||
field.addEventListener('blur', function() {
|
||||
@@ -67,15 +57,8 @@ function initializeFormValidation() {
|
||||
|
||||
function validateField(field) {
|
||||
const isValid = field.checkValidity();
|
||||
field.classList.remove('is-valid', 'is-invalid');
|
||||
field.classList.add(isValid ? 'is-valid' : 'is-invalid');
|
||||
|
||||
// Show/hide custom feedback
|
||||
const feedback = field.parentNode.querySelector('.invalid-feedback');
|
||||
if (feedback) {
|
||||
feedback.classList.toggle('hidden', isValid);
|
||||
feedback.classList.toggle('visible', !isValid);
|
||||
}
|
||||
field.setAttribute('aria-invalid', String(!isValid));
|
||||
field.classList.toggle('border-danger-500', !isValid);
|
||||
}
|
||||
|
||||
// API helpers
|
||||
@@ -157,45 +140,18 @@ function logout() {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
// Notification system
|
||||
// Notification system (delegates to shared alerts utility)
|
||||
function showNotification(message, type = 'info', duration = 5000) {
|
||||
const notificationContainer = getOrCreateNotificationContainer();
|
||||
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `alert alert-${type} alert-dismissible fade show`;
|
||||
notification.setAttribute('role', 'alert');
|
||||
notification.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
`;
|
||||
|
||||
notificationContainer.appendChild(notification);
|
||||
|
||||
// Auto-dismiss after duration
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
notification.remove();
|
||||
}, duration);
|
||||
if (window.alerts && typeof window.alerts.show === 'function') {
|
||||
return window.alerts.show(message, type, { duration });
|
||||
}
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
function getOrCreateNotificationContainer() {
|
||||
let container = document.querySelector('#notification-container');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.id = 'notification-container';
|
||||
container.className = 'position-fixed top-0 end-0 p-3';
|
||||
container.classList.add('notification-container');
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
return container;
|
||||
// Fallback if alerts module not yet loaded
|
||||
return alert(String(message));
|
||||
}
|
||||
|
||||
// Loading states
|
||||
function showLoading(element, text = 'Loading...') {
|
||||
const spinner = `<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>`;
|
||||
const spinner = `<span class="inline-block animate-spin w-4 h-4 border-2 border-current border-t-transparent rounded-full mr-2"></span>`;
|
||||
const originalContent = element.innerHTML;
|
||||
element.innerHTML = `${spinner}${text}`;
|
||||
element.disabled = true;
|
||||
@@ -271,11 +227,12 @@ function addRowSelection(table) {
|
||||
tbody.addEventListener('click', function(e) {
|
||||
const row = e.target.closest('tr');
|
||||
if (row && e.target.type !== 'checkbox') {
|
||||
row.classList.toggle('table-active');
|
||||
const isSelected = row.classList.toggle('bg-neutral-100');
|
||||
row.classList.toggle('dark:bg-neutral-700', isSelected);
|
||||
|
||||
// Trigger custom event
|
||||
const event = new CustomEvent('rowSelect', {
|
||||
detail: { row, selected: row.classList.contains('table-active') }
|
||||
detail: { row, selected: isSelected }
|
||||
});
|
||||
table.dispatchEvent(event);
|
||||
}
|
||||
@@ -342,18 +299,18 @@ function initializeSearch(searchInput, resultsContainer, searchFunction) {
|
||||
|
||||
function displaySearchResults(container, results) {
|
||||
if (!results || results.length === 0) {
|
||||
container.innerHTML = '<p class="text-muted">No results found</p>';
|
||||
container.innerHTML = '<p class="text-neutral-500">No results found</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
const resultsHtml = results.map(result => `
|
||||
<div class="search-result p-2 border-bottom">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<strong>${result.title}</strong>
|
||||
<small class="text-muted d-block">${result.description}</small>
|
||||
<small class="text-neutral-500 block">${result.description}</small>
|
||||
</div>
|
||||
<span class="badge bg-secondary">${result.type}</span>
|
||||
<span class="inline-block px-2 py-0.5 text-xs rounded bg-neutral-200 text-neutral-700">${result.type}</span>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
Reference in New Issue
Block a user