// Copyright (c) 2026 Gordon Bolton. MIT License. import { state, SPLASH_MIN_MS } from '../state.js'; import { checkAuthentication, handleLogin as apiHandleLogin, handleLogout as apiHandleLogout } from '../api.js'; import { startSSE, stopSSE } from '../sse.js'; import { stopHistoryRefresh, clearHistory, startHistoryRefresh } from './history.js'; import { closeStatusPanel } from './statusPanel.js'; export function fadeOutLogin() { return new Promise(resolve => { const login = document.getElementById('login-container'); login.classList.add('fade-out'); login.addEventListener('transitionend', () => { login.classList.add('hidden'); login.classList.remove('fade-out'); resolve(); }, { once: true }); }); } export function showSplash() { const splash = document.getElementById('splash-screen'); splash.classList.remove('hidden'); splash.style.opacity = '1'; splash.classList.remove('fade-out'); } export function dismissSplash(startTime) { return new Promise(resolve => { const elapsed = Date.now() - (startTime || 0); const remaining = Math.max(0, SPLASH_MIN_MS - elapsed); setTimeout(() => { const splash = document.getElementById('splash-screen'); splash.classList.add('fade-out'); // Fallback: resolve after transition duration + buffer in case // transitionend never fires (e.g. display was toggled in same frame) const TRANSITION_MS = 400; const fallback = setTimeout(() => { splash.classList.add('hidden'); resolve(); }, TRANSITION_MS + 100); splash.addEventListener('transitionend', () => { clearTimeout(fallback); splash.classList.add('hidden'); resolve(); }, { once: true }); }, remaining); }); } export async function checkAuthenticationAndInit() { const splashStart = Date.now(); try { const result = await checkAuthentication(); if (result.authenticated) { showDashboard(); showLoading(); startSSE(); await dismissSplash(splashStart); } else { await dismissSplash(splashStart); showLogin(); } } catch (err) { console.error('Authentication check failed:', err); await dismissSplash(splashStart); showLogin(); } } export async function handleLogin(e) { e.preventDefault(); const username = document.getElementById('username').value; const password = document.getElementById('password').value; const rememberMe = document.getElementById('remember-me').checked; try { const result = await apiHandleLogin(username, password, rememberMe); if (result.success) { // Fade out login, then show splash while opening SSE stream. // requestAnimationFrame ensures the browser paints the splash at // opacity:1 before dismissSplash adds fade-out, so the CSS // transition fires and transitionend is guaranteed. await fadeOutLogin(); showSplash(); await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r))); showDashboard(); showLoading(); const splashStart = Date.now(); startSSE(); await dismissSplash(splashStart); } else { showLoginError(result.error || 'Login failed'); } } catch (err) { showLoginError('Login failed. Please try again.'); console.error(err); } } export async function handleLogoutClick() { try { stopSSE(); stopHistoryRefresh(); if (state.statusRefreshHandle) { clearInterval(state.statusRefreshHandle); state.statusRefreshHandle = null; } await apiHandleLogout(); state.currentUser = null; clearHistory(); showLogin(); } catch (err) { console.error('Logout failed:', err); } } export function showLogin() { document.getElementById('login-container').classList.remove('hidden'); document.getElementById('dashboard-container').classList.add('hidden'); hideLoginError(); } export function showDashboard() { document.getElementById('login-container').classList.add('hidden'); document.getElementById('dashboard-container').classList.remove('hidden'); document.getElementById('currentUser').textContent = state.currentUser.name || '-'; // Always start with status panel hidden (guards against stale display value on re-login) const sp = document.getElementById('status-panel'); sp.classList.add('hidden'); // Also hide webhooks-section to keep them in sync (both show/hide together) const webhooksSection = document.getElementById('webhooks-section'); if (webhooksSection) webhooksSection.classList.add('hidden'); const adminControls = document.getElementById('admin-controls'); if (state.isAdmin) { adminControls.classList.remove('hidden'); } else { adminControls.classList.add('hidden'); } // Note: webhooks-section visibility is controlled by toggleStatusPanel() // Initialise days input from saved value const daysInput = document.getElementById('history-days'); if (daysInput) daysInput.value = state.historyDays; startHistoryRefresh(); } export function showLoginError(message) { const errorDiv = document.getElementById('login-error'); errorDiv.textContent = message; errorDiv.classList.remove('hidden'); } export function hideLoginError() { const errorDiv = document.getElementById('login-error'); errorDiv.classList.add('hidden'); } export function showError(message) { const errorDiv = document.getElementById('error-message'); errorDiv.textContent = message; errorDiv.classList.remove('hidden'); } export function hideError() { const errorDiv = document.getElementById('error-message'); errorDiv.classList.add('hidden'); } export function showLoading() { const loading = document.getElementById('loading'); loading.classList.remove('hidden'); } export function hideLoading() { const loading = document.getElementById('loading'); loading.classList.add('hidden'); }