File size: 2,901 Bytes
52c8dd5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | // Global functions and event listeners
document.addEventListener('DOMContentLoaded', function() {
// Initialize tooltips
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Mobile menu toggle
const mobileMenuButton = document.getElementById('mobile-menu-button');
const mobileMenu = document.getElementById('mobile-menu');
if (mobileMenuButton && mobileMenu) {
mobileMenuButton.addEventListener('click', function() {
mobileMenu.classList.toggle('hidden');
});
}
// Sidebar toggle for mobile
const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebar = document.getElementById('sidebar');
if (sidebarToggle && sidebar) {
sidebarToggle.addEventListener('click', function() {
sidebar.classList.toggle('-translate-x-full');
});
}
// Dark mode toggle
const darkModeToggle = document.getElementById('dark-mode-toggle');
if (darkModeToggle) {
darkModeToggle.addEventListener('click', function() {
document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
});
}
// Check for dark mode preference
if (localStorage.getItem('darkMode') === 'true') {
document.documentElement.classList.add('dark');
}
});
// Function to show toast notifications
function showToast(message, type = 'success') {
const toastContainer = document.getElementById('toast-container');
if (!toastContainer) return;
const colors = {
success: 'bg-green-500',
error: 'bg-red-500',
warning: 'bg-yellow-500',
info: 'bg-blue-500'
};
const toast = document.createElement('div');
toast.className = `text-white px-6 py-4 border-0 rounded relative mb-4 ${colors[type]}`;
toast.innerHTML = `
<span class="inline-block align-middle mr-8">${message}</span>
<button class="absolute bg-transparent text-2xl font-semibold leading-none right-0 top-0 mt-4 mr-6 outline-none focus:outline-none">
×
</button>
`;
toast.querySelector('button').addEventListener('click', () => {
toast.remove();
});
toastContainer.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 5000);
}
// API functions
async function fetchProjects() {
try {
const response = await fetch('/api/projects');
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching projects:', error);
showToast('Failed to load projects', 'error');
return [];
}
} |