/** * Token Status UI - Standalone * Displays token statuses, expiration times, last 4 digits */ class TokenStatusUI { private container: HTMLElement | null = null; private refreshInterval: number | null = null; /** * Create and show token status UI */ create(containerId: string = 'app'): void { this.container = document.getElementById(containerId); if (!this.container) { console.error('[TokenUI] Container not found:', containerId); return; } this.render(); // Refresh UI every second to update expiration countdown this.refreshInterval = window.setInterval(() => { this.render(); }, 1000); } /** * Render token status UI */ private render(): void { if (!this.container) return; const state = this.getState(); const pbStatus = state.pbUser.active ? '✓ Active' : '✗ Inactive'; const pbStatusColor = state.pbUser.active ? '#10b981' : '#ef4444'; const graphStatus = state.graphUser.active ? '✓ Active' : '○ Not Required'; const graphStatusColor = state.graphUser.active ? '#10b981' : '#6b7280'; this.container.innerHTML = `

Job Info Auth

User: ${state.userDisplayName}

PocketBase User Token

${pbStatus}

Token: ...${state.pbUser.lastFour}

Expires in: ${state.pbUser.expiresIn}

Microsoft Graph Token

${graphStatus}
${state.graphUser.active ? `

Token: ...${state.graphUser.lastFour}

Expires in: ${state.graphUser.expiresIn}

` : `

Graph token is optional. Login proceeded without it.

`}
Debug Info:
pb-user: ${state.pbUser.active ? 'active' : 'inactive'} | graph-user: ${state.graphUser.active ? 'active' : 'inactive'}
`; } /** * Get current token state */ private getState() { const auth = window.JobInfoAuth; const authState = auth.getState(); const calculateTimeLeft = (expiresAt) => { if (!expiresAt) return 'unknown'; const now = Date.now(); const diff = expiresAt - now; if (diff < 0) return 'expired'; const hours = Math.floor(diff / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `${hours}h ${minutes}m`; return `${minutes}m`; }; return { pbUser: { active: !!authState['pb-user'], lastFour: authState['pb-user']?.lastFourDigits || 'none', expiresIn: calculateTimeLeft(authState['pb-user']?.expiresAt), expiresAt: authState['pb-user']?.expiresAt }, graphUser: { active: !!authState['graph-user'], lastFour: authState['graph-user']?.lastFourDigits || 'none', expiresIn: calculateTimeLeft(authState['graph-user']?.expiresAt), expiresAt: authState['graph-user']?.expiresAt }, userDisplayName: authState.userDisplayName || 'Unknown' }; } /** * Destroy UI and cleanup */ destroy(): void { if (this.refreshInterval) { clearInterval(this.refreshInterval); } } } const TokenUI = new TokenStatusUI(); // Expose globally if (typeof window !== 'undefined') { window.TokenUI = TokenUI; } export default TokenUI;