// Copyright 2026 Versity Software // This file is licensed under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. /** * VersityGW Admin - Application Utilities */ // ============================================ // Navigation & Auth Guards // ============================================ /** * Check if user is authenticated, redirect to login if not * Also loads user context (user type and accessible gateways) */ function requireAuth() { if (!api.loadCredentials()) { window.location.href = 'index.html'; return false; } api.loadUserContext(); return true; } /** * Require admin role, redirect non-admins to explorer * Call this on admin-only pages (dashboard, users, buckets, settings) * Also loads user context (user type and accessible gateways) */ function requireAdmin() { if (!api.loadCredentials()) { window.location.href = 'index.html'; return false; } api.loadUserContext(); if (!api.isAdmin()) { window.location.href = 'explorer.html'; return false; } return true; } /** * Redirect to appropriate page if already authenticated * Admin users go to dashboard, regular users go to explorer */ function redirectIfAuthenticated() { if (api.loadCredentials()) { if (api.isAdmin()) { window.location.href = 'dashboard.html'; } else { window.location.href = 'explorer.html'; } return true; } return false; } // ============================================ // Toast Notifications // ============================================ let toastContainer = null; function initToasts() { if (!toastContainer) { toastContainer = document.createElement('div'); toastContainer.id = 'toast-container'; toastContainer.className = 'fixed top-4 right-4 z-50 flex flex-col gap-2'; document.body.appendChild(toastContainer); } } function showToast(message, type = 'info') { initToasts(); const toast = document.createElement('div'); const bgColors = { success: 'bg-green-50 border-green-500 text-green-800', error: 'bg-red-50 border-red-500 text-red-800', warning: 'bg-yellow-50 border-yellow-500 text-yellow-800', info: 'bg-blue-50 border-blue-500 text-blue-800' }; const icons = { success: ``, error: ``, warning: ``, info: `` }; toast.className = `flex items-center gap-3 px-4 py-3 rounded-lg border-l-4 shadow-lg max-w-sm animate-slide-in ${bgColors[type]}`; toast.innerHTML = ` ${icons[type]}
${escapeHtml(message)}
`; toastContainer.appendChild(toast); // Auto-remove after 5 seconds setTimeout(() => { toast.classList.add('animate-fade-out'); setTimeout(() => toast.remove(), 300); }, 5000); } // ============================================ // Modal Utilities // ============================================ function openModal(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.remove('hidden'); // Focus first input const firstInput = modal.querySelector('input:not([readonly]), select'); if (firstInput) setTimeout(() => firstInput.focus(), 100); } } function closeModal(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.add('hidden'); } } function closeAllModals() { document.querySelectorAll('[id$="-modal"]').forEach(modal => { modal.classList.add('hidden'); }); } // Close modals on Escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeAllModals(); }); // ============================================ // Loading States // ============================================ function setLoading(element, loading) { if (loading) { element.disabled = true; element.dataset.originalText = element.innerHTML; element.innerHTML = ` `; } else { element.disabled = false; if (element.dataset.originalText) { element.innerHTML = element.dataset.originalText; } } } function showTableLoading(tableBodyId, columns) { const tbody = document.getElementById(tableBodyId); if (!tbody) return; tbody.innerHTML = ''; for (let i = 0; i < 5; i++) { const row = document.createElement('tr'); row.className = 'border-b border-gray-50'; for (let j = 0; j < columns; j++) { row.innerHTML += `${escapeHtml(message)}
${escapeHtml(accessKeyShort)}
${roleLabel}
${escapeHtml(message)}