mirror of
https://github.com/myronblair/tomsjavajive
synced 2026-06-30 17:50:32 -05:00
201 lines
5.6 KiB
JavaScript
201 lines
5.6 KiB
JavaScript
/**
|
|
* Tom's Java Jive - Admin JavaScript
|
|
*/
|
|
|
|
// Sidebar Toggle
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const sidebarToggle = document.getElementById('sidebarToggle');
|
|
const sidebar = document.querySelector('.admin-sidebar');
|
|
|
|
if (sidebarToggle && sidebar) {
|
|
sidebarToggle.addEventListener('click', function() {
|
|
sidebar.classList.toggle('open');
|
|
});
|
|
|
|
// Close sidebar when clicking outside on mobile
|
|
document.addEventListener('click', function(e) {
|
|
if (window.innerWidth <= 1024) {
|
|
if (!sidebar.contains(e.target) && !sidebarToggle.contains(e.target)) {
|
|
sidebar.classList.remove('open');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Confirm dialogs
|
|
document.querySelectorAll('[data-confirm]').forEach(el => {
|
|
el.addEventListener('click', function(e) {
|
|
if (!confirm(this.dataset.confirm)) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Auto-hide alerts
|
|
document.querySelectorAll('.alert').forEach(alert => {
|
|
setTimeout(() => {
|
|
alert.style.opacity = '0';
|
|
alert.style.transform = 'translateY(-10px)';
|
|
setTimeout(() => alert.remove(), 300);
|
|
}, 5000);
|
|
});
|
|
});
|
|
|
|
// Toast notifications
|
|
const AdminToast = {
|
|
container: null,
|
|
|
|
init() {
|
|
if (!this.container) {
|
|
this.container = document.createElement('div');
|
|
this.container.style.cssText = 'position:fixed;bottom:20px;right:20px;z-index:1000;display:flex;flex-direction:column;gap:8px;';
|
|
document.body.appendChild(this.container);
|
|
}
|
|
},
|
|
|
|
show(message, type = 'success', duration = 3000) {
|
|
this.init();
|
|
|
|
const colors = {
|
|
success: '#10B981',
|
|
error: '#EF4444',
|
|
warning: '#F59E0B',
|
|
info: '#3B82F6'
|
|
};
|
|
|
|
const toast = document.createElement('div');
|
|
toast.style.cssText = `
|
|
background: white;
|
|
padding: 12px 16px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 10px 15px rgba(0,0,0,0.1);
|
|
border-left: 4px solid ${colors[type] || colors.info};
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
animation: slideIn 0.3s ease;
|
|
`;
|
|
toast.innerHTML = `
|
|
<i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}" style="color:${colors[type]}"></i>
|
|
<span>${message}</span>
|
|
`;
|
|
|
|
this.container.appendChild(toast);
|
|
|
|
setTimeout(() => {
|
|
toast.style.animation = 'slideIn 0.3s ease reverse';
|
|
setTimeout(() => toast.remove(), 300);
|
|
}, duration);
|
|
},
|
|
|
|
success(msg) { this.show(msg, 'success'); },
|
|
error(msg) { this.show(msg, 'error'); },
|
|
warning(msg) { this.show(msg, 'warning'); },
|
|
info(msg) { this.show(msg, 'info'); }
|
|
};
|
|
|
|
// API helper
|
|
async function adminFetch(url, options = {}) {
|
|
const defaults = {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
}
|
|
};
|
|
|
|
const response = await fetch(url, { ...defaults, ...options });
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.error || 'Request failed');
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Loading state
|
|
function setLoading(button, isLoading) {
|
|
if (isLoading) {
|
|
button.dataset.originalHtml = button.innerHTML;
|
|
button.innerHTML = '<span class="loading"></span> Loading...';
|
|
button.disabled = true;
|
|
} else {
|
|
button.innerHTML = button.dataset.originalHtml || button.innerHTML;
|
|
button.disabled = false;
|
|
}
|
|
}
|
|
|
|
// Format currency
|
|
function formatCurrency(amount) {
|
|
return '$' + parseFloat(amount).toFixed(2);
|
|
}
|
|
|
|
// Format date
|
|
function formatDate(dateString) {
|
|
return new Date(dateString).toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric'
|
|
});
|
|
}
|
|
|
|
// Debounce
|
|
function debounce(func, wait) {
|
|
let timeout;
|
|
return function(...args) {
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(() => func.apply(this, args), wait);
|
|
};
|
|
}
|
|
|
|
// Modal
|
|
const Modal = {
|
|
open(modalId) {
|
|
const modal = document.getElementById(modalId);
|
|
if (modal) {
|
|
modal.classList.add('active');
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
},
|
|
|
|
close(modalId) {
|
|
const modal = document.getElementById(modalId);
|
|
if (modal) {
|
|
modal.classList.remove('active');
|
|
document.body.style.overflow = '';
|
|
}
|
|
}
|
|
};
|
|
|
|
// Close modals on overlay click
|
|
document.addEventListener('click', function(e) {
|
|
if (e.target.classList.contains('modal-overlay')) {
|
|
e.target.classList.remove('active');
|
|
document.body.style.overflow = '';
|
|
}
|
|
});
|
|
|
|
// Image preview
|
|
function previewImage(input, previewId) {
|
|
const preview = document.getElementById(previewId);
|
|
if (!preview) return;
|
|
|
|
if (input.files && input.files[0]) {
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
preview.src = e.target.result;
|
|
preview.style.display = 'block';
|
|
};
|
|
reader.readAsDataURL(input.files[0]);
|
|
}
|
|
}
|
|
|
|
// Add slideIn animation
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
@keyframes slideIn {
|
|
from { transform: translateX(100%); opacity: 0; }
|
|
to { transform: translateX(0); opacity: 1; }
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|