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:
+100
-93
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user