Files
myworkspace/LinkSyncServer/static/js/dashboard.js
DavidSaylor fe4cbc3537 feat: add web UI, query engine, session management, and 20 E2E tests
- Web UI: login, dashboard, links CRUD, collections, API keys, admin pages
- Query engine: AND/OR/XOR with field filters, tag search, preview endpoint
- Session management: token expiry detection, 401 interceptor, expiry banner
- Links search: tags included, multi-word AND, query mode with set operations
- Collections: static/dynamic, query builder with preview, public tree view
- Save as Collection: convert search results (static) or query (dynamic)
- Dashboard stats: resilient loading with allSettled pattern
- Login page: redesigned with public collections tree view
- Bug fix: query executor None fields crash (notes/description/url/title)
- E2E tests: 20 Playwright tests covering all critical user flows
- All 104 tests passing (84 unit/integration + 20 E2E)
2026-05-22 07:46:53 -05:00

46 lines
1.8 KiB
JavaScript

document.addEventListener('DOMContentLoaded', async function() {
const user = JSON.parse(localStorage.getItem('user') || 'null');
if (!user) {
window.location.href = '/login';
return;
}
const token = localStorage.getItem('token');
if (token && LinkSync.isTokenExpired(token)) {
LinkSync.logout();
return;
}
document.getElementById('current-user').textContent = user.username;
if (user.role === 'admin') {
document.getElementById('admin-section').style.display = '';
}
try {
const [linksResult, collectionsResult, keysResult] = await Promise.allSettled([
LinkSync.getLinks({ limit: 1 }),
LinkSync.getCollections(),
LinkSync.getApiKeys(),
]);
const links = linksResult.status === 'fulfilled' ? linksResult.value : [];
const collections = collectionsResult.status === 'fulfilled' ? collectionsResult.value : [];
const keys = keysResult.status === 'fulfilled' ? keysResult.value : [];
document.getElementById('link-count').textContent = Array.isArray(links) ? links.length : 0;
document.getElementById('collection-count').textContent = Array.isArray(collections) ? collections.length : 0;
document.getElementById('api-key-count').textContent = Array.isArray(keys) ? keys.length : 0;
} catch (err) {
console.error('Failed to load stats:', err);
}
const expirySeconds = LinkSync.getTokenExpirySeconds(token);
if (expirySeconds > 0 && expirySeconds < 120) {
const warning = document.createElement('div');
warning.className = 'info-message';
warning.textContent = `Session expires in ${Math.ceil(expirySeconds / 60)} minute${expirySeconds > 60 ? 's' : ''}. Save your work.`;
document.querySelector('.dashboard-header').prepend(warning);
}
});