325 lines
11 KiB
JavaScript
325 lines
11 KiB
JavaScript
/**
|
||
* Admin Core Module
|
||
* Contains core utilities, navigation, viewport management, and initialization coordination
|
||
*/
|
||
|
||
// Global admin state
|
||
let adminAppState = {
|
||
currentSection: 'dashboard'
|
||
};
|
||
|
||
// Utility function to create a local date from YYYY-MM-DD string
|
||
// This prevents timezone issues when displaying dates
|
||
function createLocalDate(dateString) {
|
||
if (!dateString) return null;
|
||
const parts = dateString.split('-');
|
||
if (parts.length !== 3) return new Date(dateString); // fallback to original behavior
|
||
// Create date using local timezone (year, month-1, day)
|
||
return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]));
|
||
}
|
||
|
||
// A function to set viewport dimensions for admin page
|
||
function setAdminViewportDimensions() {
|
||
const doc = document.documentElement;
|
||
|
||
// Set height and width
|
||
doc.style.setProperty('--app-height', `${window.innerHeight}px`);
|
||
doc.style.setProperty('--app-width', `${window.innerWidth}px`);
|
||
|
||
// Handle safe area insets for devices with notches or home indicators
|
||
if (CSS.supports('padding: env(safe-area-inset-top)')) {
|
||
doc.style.setProperty('--safe-area-top', 'env(safe-area-inset-top)');
|
||
doc.style.setProperty('--safe-area-bottom', 'env(safe-area-inset-bottom)');
|
||
doc.style.setProperty('--safe-area-left', 'env(safe-area-inset-left)');
|
||
doc.style.setProperty('--safe-area-right', 'env(safe-area-inset-right)');
|
||
} else {
|
||
doc.style.setProperty('--safe-area-top', '0px');
|
||
doc.style.setProperty('--safe-area-bottom', '0px');
|
||
doc.style.setProperty('--safe-area-left', '0px');
|
||
doc.style.setProperty('--safe-area-right', '0px');
|
||
}
|
||
}
|
||
|
||
// Add mobile menu functionality
|
||
function setupMobileMenu() {
|
||
const menuToggle = document.getElementById('mobile-menu-toggle');
|
||
const sidebar = document.getElementById('admin-sidebar');
|
||
const closeSidebar = document.getElementById('close-sidebar');
|
||
const adminNavLinks = document.querySelectorAll('.admin-nav a');
|
||
|
||
if (menuToggle && sidebar) {
|
||
// Toggle menu
|
||
menuToggle.addEventListener('click', () => {
|
||
sidebar.classList.toggle('active');
|
||
menuToggle.classList.toggle('active');
|
||
document.body.classList.toggle('sidebar-open');
|
||
});
|
||
|
||
// Close sidebar button
|
||
if (closeSidebar) {
|
||
closeSidebar.addEventListener('click', () => {
|
||
sidebar.classList.remove('active');
|
||
menuToggle.classList.remove('active');
|
||
document.body.classList.remove('sidebar-open');
|
||
});
|
||
}
|
||
|
||
// Close sidebar when clicking outside
|
||
document.addEventListener('click', (e) => {
|
||
if (sidebar.classList.contains('active') &&
|
||
!sidebar.contains(e.target) &&
|
||
!menuToggle.contains(e.target)) {
|
||
sidebar.classList.remove('active');
|
||
menuToggle.classList.remove('active');
|
||
document.body.classList.remove('sidebar-open');
|
||
}
|
||
});
|
||
|
||
// Close sidebar when navigation link is clicked on mobile
|
||
adminNavLinks.forEach(link => {
|
||
link.addEventListener('click', () => {
|
||
if (window.innerWidth <= 768) {
|
||
sidebar.classList.remove('active');
|
||
menuToggle.classList.remove('active');
|
||
document.body.classList.remove('sidebar-open');
|
||
}
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
// Setup navigation between admin sections
|
||
function setupNavigation() {
|
||
const navLinks = document.querySelectorAll('.admin-nav a');
|
||
const sections = document.querySelectorAll('.admin-section');
|
||
|
||
navLinks.forEach(link => {
|
||
link.addEventListener('click', (e) => {
|
||
e.preventDefault();
|
||
|
||
const targetId = link.getAttribute('href').substring(1);
|
||
|
||
// Update active nav
|
||
navLinks.forEach(l => l.classList.remove('active'));
|
||
link.classList.add('active');
|
||
|
||
// Show target section
|
||
sections.forEach(section => {
|
||
section.style.display = section.id === targetId ? 'block' : 'none';
|
||
});
|
||
|
||
// Update URL hash and app state
|
||
window.location.hash = targetId;
|
||
adminAppState.currentSection = targetId;
|
||
|
||
// Load section-specific data
|
||
loadSectionData(targetId);
|
||
|
||
// Close mobile menu if open
|
||
const sidebar = document.getElementById('admin-sidebar');
|
||
if (sidebar && sidebar.classList.contains('open')) {
|
||
sidebar.classList.remove('open');
|
||
}
|
||
});
|
||
});
|
||
|
||
// Set initial active state based on current hash or default
|
||
const currentHash = window.location.hash || '#dashboard';
|
||
const activeLink = document.querySelector(`.admin-nav a[href="${currentHash}"]`);
|
||
if (activeLink) {
|
||
activeLink.classList.add('active');
|
||
}
|
||
|
||
// Check for initial hash routing
|
||
const hash = window.location.hash;
|
||
if (hash) {
|
||
const sectionId = hash.substring(1);
|
||
adminAppState.currentSection = sectionId;
|
||
if (sectionId === 'shifts') {
|
||
showSection('shifts');
|
||
}
|
||
}
|
||
}
|
||
|
||
// Helper function to show a specific section
|
||
function showSection(sectionId) {
|
||
const sections = document.querySelectorAll('.admin-section');
|
||
const navLinks = document.querySelectorAll('.admin-nav a');
|
||
|
||
// Hide all sections
|
||
sections.forEach(section => {
|
||
section.style.display = section.id === sectionId ? 'block' : 'none';
|
||
});
|
||
|
||
// Update active nav
|
||
navLinks.forEach(link => {
|
||
const linkTarget = link.getAttribute('href').substring(1);
|
||
link.classList.toggle('active', linkTarget === sectionId);
|
||
});
|
||
|
||
// Update app state
|
||
adminAppState.currentSection = sectionId;
|
||
|
||
// Load section-specific data
|
||
loadSectionData(sectionId);
|
||
}
|
||
|
||
// Load section-specific data based on current section
|
||
function loadSectionData(sectionId) {
|
||
switch(sectionId) {
|
||
case 'walk-sheet':
|
||
if (typeof checkAndLoadWalkSheetConfig === 'function') {
|
||
checkAndLoadWalkSheetConfig();
|
||
}
|
||
break;
|
||
case 'dashboard':
|
||
if (typeof loadDashboardData === 'function') {
|
||
loadDashboardData();
|
||
}
|
||
break;
|
||
case 'shifts':
|
||
if (typeof loadAdminShifts === 'function') {
|
||
console.log('Loading shifts for admin panel...');
|
||
loadAdminShifts();
|
||
}
|
||
break;
|
||
case 'users':
|
||
if (typeof loadUsers === 'function') {
|
||
loadUsers();
|
||
}
|
||
break;
|
||
case 'convert-data':
|
||
// Initialize data convert event listeners when section is shown
|
||
setTimeout(() => {
|
||
if (typeof window.setupDataConvertEventListeners === 'function') {
|
||
console.log('Setting up data convert event listeners...');
|
||
window.setupDataConvertEventListeners();
|
||
} else {
|
||
console.error('setupDataConvertEventListeners not found');
|
||
}
|
||
}, 100);
|
||
break;
|
||
case 'cuts':
|
||
// Initialize admin cuts manager when section is shown
|
||
setTimeout(() => {
|
||
if (typeof window.adminCutsManager === 'object' && window.adminCutsManager.initialize) {
|
||
if (!window.adminCutsManager.isInitialized) {
|
||
console.log('Initializing admin cuts manager from showSection...');
|
||
window.adminCutsManager.initialize().catch(error => {
|
||
console.error('Failed to initialize cuts manager:', error);
|
||
});
|
||
} else {
|
||
console.log('Admin cuts manager already initialized');
|
||
}
|
||
} else {
|
||
console.error('adminCutsManager not found in showSection');
|
||
}
|
||
}, 100);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Show status message
|
||
function showStatus(message, type = 'info') {
|
||
let container = document.getElementById('status-container');
|
||
|
||
// Create container if it doesn't exist
|
||
if (!container) {
|
||
container = document.createElement('div');
|
||
container.id = 'status-container';
|
||
container.className = 'status-container';
|
||
// Ensure proper z-index even if CSS hasn't loaded
|
||
container.style.zIndex = '12300';
|
||
document.body.appendChild(container);
|
||
}
|
||
|
||
const messageDiv = document.createElement('div');
|
||
messageDiv.className = `status-message ${type}`;
|
||
messageDiv.textContent = message;
|
||
|
||
// Add click to dismiss functionality
|
||
messageDiv.addEventListener('click', () => {
|
||
messageDiv.remove();
|
||
});
|
||
|
||
// Add a small close button for better UX
|
||
const closeBtn = document.createElement('span');
|
||
closeBtn.innerHTML = ' ×';
|
||
closeBtn.style.float = 'right';
|
||
closeBtn.style.fontWeight = 'bold';
|
||
closeBtn.style.marginLeft = '10px';
|
||
closeBtn.style.cursor = 'pointer';
|
||
closeBtn.setAttribute('title', 'Click to dismiss');
|
||
messageDiv.appendChild(closeBtn);
|
||
|
||
container.appendChild(messageDiv);
|
||
|
||
// Auto-remove after 5 seconds
|
||
setTimeout(() => {
|
||
if (messageDiv.parentNode) {
|
||
messageDiv.remove();
|
||
}
|
||
}, 5000);
|
||
}
|
||
|
||
// Escape HTML
|
||
function escapeHtml(text) {
|
||
if (text === null || text === undefined) {
|
||
return '';
|
||
}
|
||
const div = document.createElement('div');
|
||
div.textContent = String(text);
|
||
return div.innerHTML;
|
||
}
|
||
|
||
// Debounce function for input events
|
||
function debounce(func, wait) {
|
||
let timeout;
|
||
return function executedFunction(...args) {
|
||
const later = () => {
|
||
clearTimeout(timeout);
|
||
func(...args);
|
||
};
|
||
clearTimeout(timeout);
|
||
timeout = setTimeout(later, wait);
|
||
};
|
||
}
|
||
|
||
// Initialize the admin core when DOM is loaded
|
||
function initializeAdminCore() {
|
||
// Set initial viewport dimensions and listen for resize events
|
||
setAdminViewportDimensions();
|
||
window.addEventListener('resize', setAdminViewportDimensions);
|
||
window.addEventListener('orientationchange', () => {
|
||
// Add a small delay for orientation change to complete
|
||
setTimeout(setAdminViewportDimensions, 100);
|
||
});
|
||
|
||
setupNavigation();
|
||
setupMobileMenu();
|
||
|
||
// Check if URL has a hash to show specific section
|
||
const hash = window.location.hash;
|
||
if (hash === '#walk-sheet') {
|
||
showSection('walk-sheet');
|
||
} else if (hash === '#convert-data') {
|
||
showSection('convert-data');
|
||
} else if (hash === '#cuts') {
|
||
showSection('cuts');
|
||
} else {
|
||
// Default to dashboard
|
||
showSection('dashboard');
|
||
}
|
||
}
|
||
|
||
// Export functions for use by other modules
|
||
window.adminCore = {
|
||
showSection,
|
||
showStatus,
|
||
escapeHtml,
|
||
debounce,
|
||
createLocalDate,
|
||
initializeAdminCore,
|
||
adminAppState
|
||
};
|