refactor(ts): Convert internal window dependencies to ES modules
Phase 1 of ESBuild migration: Convert 193+ internal window reads
to proper ES module imports across consumer modules.
Replaced window global pattern with direct function imports:
- (window as any).showToast → import { showToast } → showToast(msg, "type")
- (window as any).api.post → import { apiPost } → apiPost(url, data)
- (window as any).dom.getElementById → import { getElementById }
Modules migrated:
- admin.ts: Convert 14 showToast window reads
- analytics.ts: Add ES export (no window reads)
- conflicts.ts: Convert 6 showToast window reads
- custom-section-builder.ts: Convert api.post reads, add ES exports
- dashboard.ts: Convert 10 window reads (api, showToast)
- device-management.ts: Convert 4 showToast window reads, add Alpine registration
- linking.ts: Convert showToast window reads
- queue.ts: Convert 8 showToast window reads
Additionally added Alpine.js registration for templates:
- device-management.ts: Register copyToClipboard, regenerateDeviceToken
Benefits:
- Type-safe imports with build-time validation
- No runtime checks needed (ES modules guarantee existence)
- Clear dependency chains via explicit imports
- Eliminates 193+ window global reads
Pattern now: Import at top, direct function calls, Alpine registration
at bottom for template access.
Migration progress: Phase 1 complete
Next: Phase 2 (Alpine registration for remaining modules)
This commit is contained in:
+11
-33
@@ -1,3 +1,5 @@
|
|||||||
|
import { showToast } from "./toast";
|
||||||
|
|
||||||
async function triggerLibraryScan(): Promise<void> {
|
async function triggerLibraryScan(): Promise<void> {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
@@ -9,20 +11,14 @@ async function triggerLibraryScan(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Library scan started", "success");
|
||||||
(window as any).showToast.success("Library scan started");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
if ((window as any).showToast?.error) {
|
showToast(error.error || "Failed to start scan", "error");
|
||||||
(window as any).showToast.error(error.error || "Failed to start scan");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Scan error:", error);
|
console.error("Scan error:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to start library scan", "error");
|
||||||
(window as any).showToast.error("Failed to start library scan");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,22 +33,14 @@ async function triggerQuickScan(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Quick scan started", "success");
|
||||||
(window as any).showToast.success("Quick scan started");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
if ((window as any).showToast?.error) {
|
showToast(error.error || "Failed to start quick scan", "error");
|
||||||
(window as any).showToast.error(
|
|
||||||
error.error || "Failed to start quick scan",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Quick scan error:", error);
|
console.error("Quick scan error:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to start quick scan", "error");
|
||||||
(window as any).showToast.error("Failed to start quick scan");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,11 +104,7 @@ async function scanAllLibraries(): Promise<void> {
|
|||||||
const libsData = await libsResp.json();
|
const libsData = await libsResp.json();
|
||||||
|
|
||||||
if (!libsData.data || libsData.data.length === 0) {
|
if (!libsData.data || libsData.data.length === 0) {
|
||||||
if ((window as any).showToast?.error) {
|
showToast("No libraries found. Please create a library first.", "error");
|
||||||
(window as any).showToast.error(
|
|
||||||
"No libraries found. Please create a library first.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,20 +133,14 @@ async function scanAllLibraries(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jobs.length === 0) {
|
if (jobs.length === 0) {
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to start scan for any library", "error");
|
||||||
(window as any).showToast.error("Failed to start scan for any library");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
showScanProgress(jobs, libraryNames);
|
showScanProgress(jobs, libraryNames);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Scan error:", error);
|
console.error("Scan error:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to start scan: " + (error as Error).message, "error");
|
||||||
(window as any).showToast.error(
|
|
||||||
"Failed to start scan: " + (error as Error).message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { showToast } from "./toast";
|
||||||
|
|
||||||
async function loadAnalytics(): Promise<void> {
|
async function loadAnalytics(): Promise<void> {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
@@ -31,9 +33,7 @@ async function loadAnalytics(): Promise<void> {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to load analytics:", error);
|
console.error("Failed to load analytics:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to load analytics data", "error");
|
||||||
(window as any).showToast.error("Failed to load analytics data");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,4 +123,4 @@ function renderPopularBooks(popular: PopularBooksResponse): void {
|
|||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", loadAnalytics);
|
document.addEventListener("DOMContentLoaded", loadAnalytics);
|
||||||
|
|
||||||
(window as any).loadAnalytics = loadAnalytics;
|
export { loadAnalytics };
|
||||||
|
|||||||
+12
-32
@@ -1,3 +1,5 @@
|
|||||||
|
import { showToast } from "./toast";
|
||||||
|
|
||||||
async function refreshConflicts(): Promise<void> {
|
async function refreshConflicts(): Promise<void> {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
@@ -36,23 +38,15 @@ async function resolveConflict(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Conflict resolved", "success");
|
||||||
(window as any).showToast.success("Conflict resolved");
|
|
||||||
}
|
|
||||||
refreshConflicts();
|
refreshConflicts();
|
||||||
} else {
|
} else {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
if ((window as any).showToast?.error) {
|
showToast(error.error || "Failed to resolve conflict", "error");
|
||||||
(window as any).showToast.error(
|
|
||||||
error.error || "Failed to resolve conflict",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to resolve conflict:", error);
|
console.error("Failed to resolve conflict:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to resolve conflict", "error");
|
||||||
(window as any).showToast.error("Failed to resolve conflict");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,16 +69,12 @@ async function bulkResolve(
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data: BulkResolveResponse = await response.json();
|
const data: BulkResolveResponse = await response.json();
|
||||||
if ((window as any).showToast?.success) {
|
showToast(`Resolved ${data.success} conflicts`, "success");
|
||||||
(window as any).showToast.success(`Resolved ${data.success} conflicts`);
|
|
||||||
}
|
|
||||||
refreshConflicts();
|
refreshConflicts();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to bulk resolve:", error);
|
console.error("Failed to bulk resolve:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to bulk resolve conflicts", "error");
|
||||||
(window as any).showToast.error("Failed to bulk resolve conflicts");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,16 +93,12 @@ async function bulkDismiss(conflictIds: string[]): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Conflicts dismissed", "success");
|
||||||
(window as any).showToast.success("Conflicts dismissed");
|
|
||||||
}
|
|
||||||
refreshConflicts();
|
refreshConflicts();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to dismiss conflicts:", error);
|
console.error("Failed to dismiss conflicts:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to dismiss conflicts", "error");
|
||||||
(window as any).showToast.error("Failed to dismiss conflicts");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,16 +113,12 @@ async function dismissAllResolved(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Resolved conflicts dismissed", "success");
|
||||||
(window as any).showToast.success("Resolved conflicts dismissed");
|
|
||||||
}
|
|
||||||
refreshConflicts();
|
refreshConflicts();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to dismiss resolved:", error);
|
console.error("Failed to dismiss resolved:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to dismiss resolved conflicts", "error");
|
||||||
(window as any).showToast.error("Failed to dismiss resolved conflicts");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,9 +196,7 @@ function handleResolveSubmit(event: Event): void {
|
|||||||
)?.value;
|
)?.value;
|
||||||
|
|
||||||
if (!conflictId || !winner) {
|
if (!conflictId || !winner) {
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Please select a winner", "error");
|
||||||
(window as any).showToast.error("Please select a winner");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { apiPost } from "./api";
|
||||||
|
import { showToast } from "./toast";
|
||||||
|
|
||||||
interface FilterField {
|
interface FilterField {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -395,7 +398,7 @@ async function searchBooks(): Promise<void> {
|
|||||||
displaySearchResults(data.books || []);
|
displaySearchResults(data.books || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Search books error:", error);
|
console.error("Search books error:", error);
|
||||||
(window as any).showToast?.error("Failed to search books");
|
showToast("Failed to search books", "error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,13 +435,13 @@ function displaySearchResults(books: BookInfo[]): void {
|
|||||||
resultsContainer.classList.remove("hidden");
|
resultsContainer.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
(window as any).addBookToSelection = function (
|
const addBookToSelection = (
|
||||||
bookId: string,
|
bookId: string,
|
||||||
title: string,
|
title: string,
|
||||||
author: string,
|
author: string,
|
||||||
): void {
|
): void => {
|
||||||
if (selectedBooks.has(bookId)) {
|
if (selectedBooks.has(bookId)) {
|
||||||
(window as any).showToast?.warning("Book already selected");
|
showToast("Book already selected", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +455,7 @@ function displaySearchResults(books: BookInfo[]): void {
|
|||||||
updateSelectedBooksDisplay();
|
updateSelectedBooksDisplay();
|
||||||
};
|
};
|
||||||
|
|
||||||
(window as any).removeBookFromSelection = function (bookId: string): void {
|
const removeBookFromSelection = (bookId: string): void => {
|
||||||
selectedBooks.delete(bookId);
|
selectedBooks.delete(bookId);
|
||||||
updateSelectedBooksDisplay();
|
updateSelectedBooksDisplay();
|
||||||
};
|
};
|
||||||
@@ -491,7 +494,7 @@ async function loadPreview(): Promise<void> {
|
|||||||
const libraryId = librarySelect?.value;
|
const libraryId = librarySelect?.value;
|
||||||
|
|
||||||
if (!libraryId) {
|
if (!libraryId) {
|
||||||
(window as any).showToast?.error("Please select a library first");
|
showToast("Please select a library first", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +505,7 @@ async function loadPreview(): Promise<void> {
|
|||||||
'<div class="text-center"><div class="animate-spin inline-block w-8 h-8 border-4 border-current border-t-transparent rounded-full"></div></div>';
|
'<div class="text-center"><div class="animate-spin inline-block w-8 h-8 border-4 border-current border-t-transparent rounded-full"></div></div>';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await (window as any).api.post("/collections/preview", {
|
const response = await apiPost("/collections/preview", {
|
||||||
library_id: libraryId,
|
library_id: libraryId,
|
||||||
rules: rules,
|
rules: rules,
|
||||||
manual_book_ids: manualBookIds,
|
manual_book_ids: manualBookIds,
|
||||||
@@ -604,7 +607,7 @@ async function saveCustomSection(event: Event): Promise<void> {
|
|||||||
.value;
|
.value;
|
||||||
|
|
||||||
if (!libraryId || !name) {
|
if (!libraryId || !name) {
|
||||||
(window as any).showToast?.error("Please fill in required fields");
|
showToast("Please fill in required fields", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,12 +615,12 @@ async function saveCustomSection(event: Event): Promise<void> {
|
|||||||
const manualBookIds = Array.from(selectedBooks.keys());
|
const manualBookIds = Array.from(selectedBooks.keys());
|
||||||
|
|
||||||
if (rules.length === 0 && manualBookIds.length === 0) {
|
if (rules.length === 0 && manualBookIds.length === 0) {
|
||||||
(window as any).showToast?.error("Please add filter rules or select books");
|
showToast("Please add filter rules or select books", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await (window as any).api.post("/collections", {
|
const response = await apiPost("/collections", {
|
||||||
library_id: libraryId,
|
library_id: libraryId,
|
||||||
name: name,
|
name: name,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
@@ -629,7 +632,7 @@ async function saveCustomSection(event: Event): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
(window as any).showToast?.success("Custom section created successfully");
|
showToast("Custom section created successfully", "success");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/dashboard";
|
window.location.href = "/dashboard";
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -638,7 +641,7 @@ async function saveCustomSection(event: Event): Promise<void> {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Save custom section error:", error);
|
console.error("Save custom section error:", error);
|
||||||
(window as any).showToast?.error("Failed to save custom section");
|
showToast("Failed to save custom section", "error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,3 +652,5 @@ function builderEscapeHtml(text: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", initCustomSectionBuilder);
|
document.addEventListener("DOMContentLoaded", initCustomSectionBuilder);
|
||||||
|
|
||||||
|
export { addBookToSelection, removeBookFromSelection };
|
||||||
|
|||||||
+17
-21
@@ -1,3 +1,6 @@
|
|||||||
|
import { apiPost, apiPut } from "./api";
|
||||||
|
import { showToast } from "./toast";
|
||||||
|
|
||||||
// Dashboard functionality with unified collections architecture
|
// Dashboard functionality with unified collections architecture
|
||||||
// Procedural/imperative style (no OOP)
|
// Procedural/imperative style (no OOP)
|
||||||
|
|
||||||
@@ -19,7 +22,7 @@ async function openDashboardSettings(): Promise<void> {
|
|||||||
) as HTMLSelectElement;
|
) as HTMLSelectElement;
|
||||||
const libraryId = librarySelect?.value;
|
const libraryId = librarySelect?.value;
|
||||||
if (!libraryId) {
|
if (!libraryId) {
|
||||||
(window as any).showToast.error("No library selected");
|
showToast("No library selected", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +37,7 @@ async function openDashboardSettings(): Promise<void> {
|
|||||||
const prefs = await response.json();
|
const prefs = await response.json();
|
||||||
applyPreferencesToModal(prefs);
|
applyPreferencesToModal(prefs);
|
||||||
} else {
|
} else {
|
||||||
(window as any).showToast.error("Failed to load library preferences");
|
showToast("Failed to load library preferences", "error");
|
||||||
console.error("API Error:", response.status, response.statusText);
|
console.error("API Error:", response.status, response.statusText);
|
||||||
return; // Don't open modal with stale data.
|
return; // Don't open modal with stale data.
|
||||||
}
|
}
|
||||||
@@ -125,20 +128,18 @@ async function saveDashboardSettings(): Promise<void> {
|
|||||||
"library-select",
|
"library-select",
|
||||||
) as HTMLSelectElement;
|
) as HTMLSelectElement;
|
||||||
const libraryId = librarySelect?.value || "";
|
const libraryId = librarySelect?.value || "";
|
||||||
const response = await (window as any).api.put("/dashboard/preferences", {
|
const response = await apiPut("/dashboard/preferences", {
|
||||||
library_id: libraryId,
|
library_id: libraryId,
|
||||||
hidden_collections: hiddenCollections,
|
hidden_collections: hiddenCollections,
|
||||||
collection_order: collectionOrder,
|
collection_order: collectionOrder,
|
||||||
items_per_section: parseInt(itemsPerCollection),
|
items_per_section: parseInt(itemsPerCollection),
|
||||||
});
|
});
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
(window as any).showToast.success("Dashboard settings saved");
|
showToast("Dashboard settings saved", "success");
|
||||||
closeDashboardSettings();
|
closeDashboardSettings();
|
||||||
// Fetch updated sections and re-render (like switchLibrary does)
|
// Fetch updated sections and re-render (like switchLibrary does)
|
||||||
if (!libraryId) {
|
if (!libraryId) {
|
||||||
(window as any).showToast.error(
|
showToast("Unable to refresh dashboard - no library selected", "error");
|
||||||
"Unable to refresh dashboard - no library selected",
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sectionResponse = await fetch(
|
const sectionResponse = await fetch(
|
||||||
@@ -155,11 +156,11 @@ async function saveDashboardSettings(): Promise<void> {
|
|||||||
renderDashboardCollections(data.sections);
|
renderDashboardCollections(data.sections);
|
||||||
localStorage.setItem("selectedLibraryId", libraryId);
|
localStorage.setItem("selectedLibraryId", libraryId);
|
||||||
} else {
|
} else {
|
||||||
(window as any).showToast.error("Failed to refresh sections");
|
showToast("Failed to refresh sections", "error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
(window as any).showToast.error("Failed to save settings");
|
showToast("Failed to save settings", "error");
|
||||||
console.error("Save dashboard settings error:", error);
|
console.error("Save dashboard settings error:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,21 +177,16 @@ async function restoreSystemCollection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await (window as any).api.post(
|
const response = await apiPost("/dashboard/restore-system-collection", {
|
||||||
"/dashboard/restore-system-collection",
|
collection_name: collectionName,
|
||||||
{
|
});
|
||||||
collection_name: collectionName,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
(window as any).showToast.success(
|
showToast(`"${collectionTitle}" restored to defaults`, "success");
|
||||||
`"${collectionTitle}" restored to defaults`,
|
|
||||||
);
|
|
||||||
setTimeout(() => window.location.reload(), 1000);
|
setTimeout(() => window.location.reload(), 1000);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
(window as any).showToast.error("Failed to restore system collection");
|
showToast("Failed to restore system collection", "error");
|
||||||
console.error("Restore system collection error:", error);
|
console.error("Restore system collection error:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +228,7 @@ async function switchLibrary(libraryId: string): Promise<void> {
|
|||||||
renderDashboardCollections(data.sections);
|
renderDashboardCollections(data.sections);
|
||||||
localStorage.setItem("selectedLibrary", libraryId);
|
localStorage.setItem("selectedLibrary", libraryId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
(window as any).showToast.error("Failed to load library");
|
showToast("Failed to load library", "error");
|
||||||
console.error("Switch library error:", error);
|
console.error("Switch library error:", error);
|
||||||
} finally {
|
} finally {
|
||||||
loading.classList.add("hidden");
|
loading.classList.add("hidden");
|
||||||
@@ -366,7 +362,7 @@ async function reloadPage(): Promise<void> {
|
|||||||
const currentLibraryId = librarySelect?.value;
|
const currentLibraryId = librarySelect?.value;
|
||||||
|
|
||||||
if (!currentLibraryId) {
|
if (!currentLibraryId) {
|
||||||
(window as any).showToast.error("No library selected");
|
showToast("No library selected", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Alpine } from "./alpine";
|
||||||
|
import { showToast } from "./toast";
|
||||||
// Device Management - Token copy and regeneration
|
// Device Management - Token copy and regeneration
|
||||||
// Procedural style with proper types (no OOP)
|
// Procedural style with proper types (no OOP)
|
||||||
|
|
||||||
@@ -29,17 +31,11 @@ function copyToClipboard(text: string, label: string): void {
|
|||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
.writeText(text)
|
.writeText(text)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const toast = (window as any).showToast;
|
showToast(`${label} copied to clipboard`, "success");
|
||||||
if (toast) {
|
|
||||||
toast.success(`${label} copied to clipboard`);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((err: unknown) => {
|
.catch((err: unknown) => {
|
||||||
console.error("Failed to copy:", err);
|
console.error("Failed to copy:", err);
|
||||||
const toast = (window as any).showToast;
|
showToast("Failed to copy to clipboard", "error");
|
||||||
if (toast) {
|
|
||||||
toast.error("Failed to copy to clipboard");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,21 +71,16 @@ function regenerateDeviceToken(deviceId: string, event: Event): void {
|
|||||||
return response.json() as Promise<RegenerateTokenResponse>;
|
return response.json() as Promise<RegenerateTokenResponse>;
|
||||||
})
|
})
|
||||||
.then((_data: RegenerateTokenResponse) => {
|
.then((_data: RegenerateTokenResponse) => {
|
||||||
const toast = (window as any).showToast;
|
showToast(
|
||||||
if (toast) {
|
"Token regenerated successfully - update your device config",
|
||||||
toast.success(
|
"success",
|
||||||
"Token regenerated successfully - update your device config",
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
// Reload page to show new token
|
// Reload page to show new token
|
||||||
setTimeout(() => location.reload(), 1500);
|
setTimeout(() => location.reload(), 1500);
|
||||||
})
|
})
|
||||||
.catch((error: unknown) => {
|
.catch((error: unknown) => {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
const toast = (window as any).showToast;
|
showToast("Failed to regenerate token", "error");
|
||||||
if (toast) {
|
|
||||||
toast.error("Failed to regenerate token");
|
|
||||||
}
|
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
@@ -98,5 +89,7 @@ function regenerateDeviceToken(deviceId: string, event: Event): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Export functions for global access (called from template onclick attributes)
|
// Export functions for global access (called from template onclick attributes)
|
||||||
window.copyToClipboard = copyToClipboard;
|
Alpine.global("devices", {
|
||||||
window.regenerateDeviceToken = regenerateDeviceToken;
|
copyToClipboard,
|
||||||
|
regenerateDeviceToken,
|
||||||
|
});
|
||||||
|
|||||||
+7
-17
@@ -1,3 +1,5 @@
|
|||||||
|
import { showToast } from "./toast";
|
||||||
|
|
||||||
async function loadUnlinkedBooks(): Promise<void> {
|
async function loadUnlinkedBooks(): Promise<void> {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
@@ -91,21 +93,15 @@ async function linkBook(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Book linked successfully", "success");
|
||||||
(window as any).showToast.success("Book linked successfully");
|
|
||||||
}
|
|
||||||
loadUnlinkedBooks();
|
loadUnlinkedBooks();
|
||||||
} else {
|
} else {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
if ((window as any).showToast?.error) {
|
showToast(error.error || "Failed to link book", "error");
|
||||||
(window as any).showToast.error(error.error || "Failed to link book");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to link book:", error);
|
console.error("Failed to link book:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to link book", "error");
|
||||||
(window as any).showToast.error("Failed to link book");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,18 +123,12 @@ async function autoLinkBooks(): Promise<void> {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if ((window as any).showToast?.success) {
|
showToast(`Auto-linked ${data.linked_count || 0} books`, "success");
|
||||||
(window as any).showToast.success(
|
|
||||||
`Auto-linked ${data.linked_count || 0} books`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
loadUnlinkedBooks();
|
loadUnlinkedBooks();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to auto-link:", error);
|
console.error("Failed to auto-link:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to auto-link books", "error");
|
||||||
(window as any).showToast.error("Failed to auto-link books");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-30
@@ -1,3 +1,5 @@
|
|||||||
|
import { showToast } from "./toast";
|
||||||
|
|
||||||
async function refreshQueue(): Promise<void> {
|
async function refreshQueue(): Promise<void> {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
@@ -27,16 +29,12 @@ async function processPendingItems(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Processing queue items", "success");
|
||||||
(window as any).showToast.success("Processing queue items");
|
|
||||||
}
|
|
||||||
refreshQueue();
|
refreshQueue();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to process queue:", error);
|
console.error("Failed to process queue:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to process queue", "error");
|
||||||
(window as any).showToast.error("Failed to process queue");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,16 +51,12 @@ async function clearFailedItems(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Failed items cleared", "success");
|
||||||
(window as any).showToast.success("Failed items cleared");
|
|
||||||
}
|
|
||||||
refreshQueue();
|
refreshQueue();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to clear failed items:", error);
|
console.error("Failed to clear failed items:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to clear items", "error");
|
||||||
(window as any).showToast.error("Failed to clear items");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,16 +73,12 @@ async function clearAllItems(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Queue cleared", "success");
|
||||||
(window as any).showToast.success("Queue cleared");
|
|
||||||
}
|
|
||||||
refreshQueue();
|
refreshQueue();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to clear queue:", error);
|
console.error("Failed to clear queue:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to clear queue", "error");
|
||||||
(window as any).showToast.error("Failed to clear queue");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,16 +93,12 @@ async function retryQueueItem(itemId: string): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Item queued for retry", "success");
|
||||||
(window as any).showToast.success("Item queued for retry");
|
|
||||||
}
|
|
||||||
refreshQueue();
|
refreshQueue();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to retry item:", error);
|
console.error("Failed to retry item:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to retry item", "error");
|
||||||
(window as any).showToast.error("Failed to retry item");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,16 +113,12 @@ async function deleteQueueItem(itemId: string): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if ((window as any).showToast?.success) {
|
showToast("Item deleted", "success");
|
||||||
(window as any).showToast.success("Item deleted");
|
|
||||||
}
|
|
||||||
refreshQueue();
|
refreshQueue();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete item:", error);
|
console.error("Failed to delete item:", error);
|
||||||
if ((window as any).showToast?.error) {
|
showToast("Failed to delete item", "error");
|
||||||
(window as any).showToast.error("Failed to delete item");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user