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:
+120
-115
@@ -1,170 +1,175 @@
|
||||
async function refreshQueue(): Promise<void> {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/queue/all', {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
try {
|
||||
const response = await fetch("/api/queue/all", {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
renderQueueItems(data.items || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh queue:', error);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
renderQueueItems(data.items || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh queue:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function processPendingItems(): Promise<void> {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/queue/process', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
try {
|
||||
const response = await fetch("/api/queue/process", {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success('Processing queue items');
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to process queue:', error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error('Failed to process queue');
|
||||
}
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success("Processing queue items");
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to process queue:", error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error("Failed to process queue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function clearFailedItems(): Promise<void> {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
if (!confirm('Are you sure you want to clear all failed items?')) return;
|
||||
if (!confirm("Are you sure you want to clear all failed items?")) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/queue/failed', {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
try {
|
||||
const response = await fetch("/api/queue/failed", {
|
||||
method: "DELETE",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success('Failed items cleared');
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to clear failed items:', error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error('Failed to clear items');
|
||||
}
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success("Failed items cleared");
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to clear failed items:", error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error("Failed to clear items");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function clearAllItems(): Promise<void> {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
if (!confirm('Are you sure you want to clear all queue items?')) return;
|
||||
if (!confirm("Are you sure you want to clear all queue items?")) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/queue/all', {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
try {
|
||||
const response = await fetch("/api/queue/all", {
|
||||
method: "DELETE",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success('Queue cleared');
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to clear queue:', error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error('Failed to clear queue');
|
||||
}
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success("Queue cleared");
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to clear queue:", error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error("Failed to clear queue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function retryQueueItem(itemId: string): Promise<void> {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/queue/items/${itemId}/retry`, {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
try {
|
||||
const response = await fetch(`/api/queue/items/${itemId}/retry`, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success('Item queued for retry');
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to retry item:', error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error('Failed to retry item');
|
||||
}
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success("Item queued for retry");
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to retry item:", error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error("Failed to retry item");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteQueueItem(itemId: string): Promise<void> {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/queue/items/${itemId}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
try {
|
||||
const response = await fetch(`/api/queue/items/${itemId}`, {
|
||||
method: "DELETE",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success('Item deleted');
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to delete item:', error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error('Failed to delete item');
|
||||
}
|
||||
if (response.ok) {
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success("Item deleted");
|
||||
}
|
||||
refreshQueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to delete item:", error);
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error("Failed to delete item");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderQueueItems(items: QueueItemResponse[]): void {
|
||||
const container = document.getElementById('queue-items');
|
||||
if (!container) return;
|
||||
const container = document.getElementById("queue-items");
|
||||
if (!container) return;
|
||||
|
||||
if (items.length === 0) {
|
||||
container.innerHTML = '<p class="text-center p-4" style="color: var(--text-secondary)">Queue is empty</p>';
|
||||
return;
|
||||
}
|
||||
if (items.length === 0) {
|
||||
container.innerHTML =
|
||||
'<p class="text-center p-4" style="color: var(--text-secondary)">Queue is empty</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = items.map(item => `
|
||||
container.innerHTML = items
|
||||
.map(
|
||||
(item) => `
|
||||
<div class="p-3 rounded-lg border mb-2" style="background-color: var(--bg-secondary); border-color: var(--border)">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<p class="font-medium" style="color: var(--text-primary)">${item.media_title || 'Unknown'}</p>
|
||||
<p class="font-medium" style="color: var(--text-primary)">${item.media_title || "Unknown"}</p>
|
||||
<p class="text-sm" style="color: var(--text-secondary)">${item.status} - ${item.sync_type}</p>
|
||||
<p class="text-xs" style="color: var(--text-secondary)">Attempts: ${item.attempts}/${item.max_attempts}</p>
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
${item.status === 'failed' ? `<button onclick="window.retryQueueItem('${item.id}')" class="btn-secondary px-2 py-1 rounded text-sm">Retry</button>` : ''}
|
||||
${item.status === "failed" ? `<button onclick="window.retryQueueItem('${item.id}')" class="btn-secondary px-2 py-1 rounded text-sm">Retry</button>` : ""}
|
||||
<button onclick="window.deleteQueueItem('${item.id}')" class="btn-secondary px-2 py-1 rounded text-sm">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
${item.error_message ? `<p class="text-xs mt-2" style="color: var(--accent)">${item.error_message}</p>` : ''}
|
||||
${item.error_message ? `<p class="text-xs mt-2" style="color: var(--accent)">${item.error_message}</p>` : ""}
|
||||
</div>
|
||||
`).join('');
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
(window as any).refreshQueue = refreshQueue;
|
||||
|
||||
Reference in New Issue
Block a user