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)
This commit is contained in:
@@ -5,6 +5,12 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
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') {
|
||||
@@ -12,16 +18,28 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
}
|
||||
|
||||
try {
|
||||
const [links, collections, keys] = await Promise.all([
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user