feat(web): update frontend TypeScript modules and API types

This commit updates the web frontend TypeScript modules:

Core modules:
- admin.ts: Admin panel functionality and user management
- analytics.ts: Analytics dashboard and data visualization
- api-explorer.ts: Interactive API documentation explorer
- api.ts: Core API client with request/response handling
- collections.ts: Book collection management UI
- conflicts.ts: Sync conflict resolution interface
- custom-section-builder.ts: Dynamic section builder for UI
- docs.ts: Documentation viewer and navigation
- dom.ts: DOM manipulation utilities and helpers
- header.ts: Application header with navigation
- library.ts: Library view and book grid management
- linking.ts: Device-book linking interface
- password_validation.ts: Client-side password strength validation
- queue.ts: Device sync queue management UI
- search.ts: Full-text search with Lunr integration
- storage.ts: Local storage and cache management
- theme.ts: Theme management and CSS variable updates
- themeDropdown.ts: Theme selector dropdown component
- toast.ts: Toast notification system
- woodPaneling.ts: Visual theme effects
- woodPanelingInit.ts: Visual effects initialization

Type definitions:
- api.d.ts: Updated TypeScript definitions for API responses

These updates enhance the frontend with improved functionality
for book management, device synchronization, and user experience.
This commit is contained in:
2026-02-27 17:06:48 -05:00
parent 4d321528b2
commit ea5ad7a41b
22 changed files with 3133 additions and 2737 deletions
+100 -93
View File
@@ -1,133 +1,140 @@
// Theme management functionality
type ThemeType =
| 'tokyo-night'
| 'dracula'
| 'nord'
| 'solarized-dark'
| 'monokai'
| 'one-dark-pro'
| 'material-dark'
| 'catppuccin-mocha'
| 'catppuccin-macchiato'
| 'catppuccin-frappe'
| 'catppuccin-latte';
| "tokyo-night"
| "dracula"
| "nord"
| "solarized-dark"
| "monokai"
| "one-dark-pro"
| "material-dark"
| "catppuccin-mocha"
| "catppuccin-macchiato"
| "catppuccin-frappe"
| "catppuccin-latte";
const DEFAULT_THEME: ThemeType = 'tokyo-night';
const THEME_STORAGE_KEY = 'theme';
const TOKEN_STORAGE_KEY = 'token';
const DEFAULT_THEME: ThemeType = "tokyo-night";
const THEME_STORAGE_KEY = "theme";
const TOKEN_STORAGE_KEY = "token";
// Apply theme to document body
const applyTheme = (theme: string): void => {
// Apply regular theme only
document.body.className = `theme-${theme}`;
document.body.style.background = '';
document.body.style.backgroundSize = '';
document.body.style.backgroundAttachment = '';
// Apply regular theme only
document.body.className = `theme-${theme}`;
document.body.style.background = "";
document.body.style.backgroundSize = "";
document.body.style.backgroundAttachment = "";
localStorage.setItem(THEME_STORAGE_KEY, theme);
localStorage.setItem(THEME_STORAGE_KEY, theme);
};
// Load theme from localStorage or use default
const loadTheme = (): void => {
const storedTheme = localStorage.getItem(THEME_STORAGE_KEY) as ThemeType | null;
const theme = storedTheme || DEFAULT_THEME;
applyTheme(theme);
const storedTheme = localStorage.getItem(
THEME_STORAGE_KEY,
) as ThemeType | null;
const theme = storedTheme || DEFAULT_THEME;
applyTheme(theme);
};
// Handle theme change from user selection
const changeTheme = async (): Promise<void> => {
const themeSelect = document.getElementById('theme-select') as HTMLSelectElement;
if (!themeSelect) return;
const theme = themeSelect.value as ThemeType;
applyTheme(theme);
// Save to server if logged in
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
if (!token) return;
try {
const response = await fetch('/api/auth/theme', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ theme })
});
if (!response.ok) {
console.log('Theme save failed');
}
} catch (error) {
console.log('Theme save failed', error);
const themeSelect = document.getElementById(
"theme-select",
) as HTMLSelectElement;
if (!themeSelect) return;
const theme = themeSelect.value as ThemeType;
applyTheme(theme);
// Save to server if logged in
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
if (!token) return;
try {
const response = await fetch("/api/auth/theme", {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ theme }),
});
if (!response.ok) {
console.log("Theme save failed");
}
} catch (error) {
console.log("Theme save failed", error);
}
};
// Load user's theme from server if logged in
const loadUserTheme = async (): Promise<void> => {
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
if (!token) return;
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
if (!token) return;
try {
const response = await fetch('/api/auth/profile', {
headers: { 'Authorization': `Bearer ${token}` }
});
try {
const response = await fetch("/api/auth/profile", {
headers: { Authorization: `Bearer ${token}` },
});
if (response.ok) {
const data = await response.json();
if (data.theme) {
applyTheme(data.theme as string);
}
}
} catch {
// Silently fail - user will get default theme
if (response.ok) {
const data = await response.json();
if (data.theme) {
applyTheme(data.theme as string);
}
}
} catch {
// Silently fail - user will get default theme
}
};
// Initialize theme system
const initializeTheme = (): void => {
loadTheme();
loadUserTheme();
// Set theme select value to current theme
const themeSelect = document.getElementById('theme-select') as HTMLSelectElement;
if (themeSelect) {
const currentTheme = localStorage.getItem(THEME_STORAGE_KEY) || DEFAULT_THEME;
themeSelect.value = currentTheme;
}
// Setup smooth scroll for anchor links
setupSmoothScroll();
loadTheme();
loadUserTheme();
// Set theme select value to current theme
const themeSelect = document.getElementById(
"theme-select",
) as HTMLSelectElement;
if (themeSelect) {
const currentTheme =
localStorage.getItem(THEME_STORAGE_KEY) || DEFAULT_THEME;
themeSelect.value = currentTheme;
}
// Setup smooth scroll for anchor links
setupSmoothScroll();
};
// Setup smooth scrolling for anchor links
const setupSmoothScroll = (): void => {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', (e) => {
e.preventDefault();
const href = anchor.getAttribute('href');
if (!href) return;
const target = document.querySelector(href);
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", (e) => {
e.preventDefault();
const href = anchor.getAttribute("href");
if (!href) return;
const target = document.querySelector(href);
if (target) {
target.scrollIntoView({
behavior: "smooth",
block: "start",
});
}
});
});
};
// Auto-initialize when DOM is ready
if (typeof document !== 'undefined') {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeTheme);
} else {
initializeTheme();
}
if (typeof document !== "undefined") {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initializeTheme);
} else {
initializeTheme();
}
}
// Make changeTheme available globally for HTML onchange attribute