fixed login loop
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* 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 = `
|
||||
<div style="
|
||||
min-h-screen;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
">
|
||||
<div style="
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
||||
padding: 40px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
">
|
||||
<!-- Header -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h1 style="font-size: 28px; font-weight: bold; color: #1f2937; margin: 0 0 10px 0;">
|
||||
Job Info Auth
|
||||
</h1>
|
||||
<p style="color: #6b7280; margin: 0; font-size: 14px;">
|
||||
User: <strong>${state.userDisplayName}</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- PocketBase User Token -->
|
||||
<div style="
|
||||
margin-bottom: 24px;
|
||||
padding: 20px;
|
||||
background: #f9fafb;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid ${pbStatusColor};
|
||||
">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||
<h2 style="font-size: 14px; font-weight: 600; color: #1f2937; margin: 0;">
|
||||
PocketBase User Token
|
||||
</h2>
|
||||
<span style="color: ${pbStatusColor}; font-weight: 600; font-size: 13px;">
|
||||
${pbStatus}
|
||||
</span>
|
||||
</div>
|
||||
<p style="color: #6b7280; font-size: 12px; margin: 8px 0; font-family: monospace;">
|
||||
Token: ...${state.pbUser.lastFour}
|
||||
</p>
|
||||
<p style="color: #6b7280; font-size: 12px; margin: 8px 0;">
|
||||
Expires in: <strong style="color: #1f2937;">${state.pbUser.expiresIn}</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Graph User Token -->
|
||||
<div style="
|
||||
margin-bottom: 24px;
|
||||
padding: 20px;
|
||||
background: #f9fafb;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid ${graphStatusColor};
|
||||
">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||
<h2 style="font-size: 14px; font-weight: 600; color: #1f2937; margin: 0;">
|
||||
Microsoft Graph Token
|
||||
</h2>
|
||||
<span style="color: ${graphStatusColor}; font-weight: 600; font-size: 13px;">
|
||||
${graphStatus}
|
||||
</span>
|
||||
</div>
|
||||
${state.graphUser.active ? `
|
||||
<p style="color: #6b7280; font-size: 12px; margin: 8px 0; font-family: monospace;">
|
||||
Token: ...${state.graphUser.lastFour}
|
||||
</p>
|
||||
<p style="color: #6b7280; font-size: 12px; margin: 8px 0;">
|
||||
Expires in: <strong style="color: #1f2937;">${state.graphUser.expiresIn}</strong>
|
||||
</p>
|
||||
` : `
|
||||
<p style="color: #6b7280; font-size: 12px; margin: 8px 0;">
|
||||
Graph token is optional. Login proceeded without it.
|
||||
</p>
|
||||
`}
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<button onclick="window.JobInfoAuth.logout(); location.reload();" style="
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
">
|
||||
Logout
|
||||
</button>
|
||||
<button onclick="location.reload();" style="
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
">
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Debug Info -->
|
||||
<div style="
|
||||
margin-top: 30px;
|
||||
padding: 15px;
|
||||
background: #f0f9ff;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #bfdbfe;
|
||||
font-size: 11px;
|
||||
color: #1e40af;
|
||||
font-family: monospace;
|
||||
word-break: break-all;
|
||||
">
|
||||
<strong>Debug Info:</strong><br>
|
||||
pb-user: ${state.pbUser.active ? 'active' : 'inactive'} | graph-user: ${state.graphUser.active ? 'active' : 'inactive'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
Reference in New Issue
Block a user