refactor: Add Alpine.js registration to existing TypeScript modules

Added Alpine.global() registration to enable template access to functions:

- admin.ts: Added Alpine for scan, stats, and settings functions
- api-explorer.ts: Already had Alpine (kept as is)
- bookshelf.ts: Added Alpine for library/bookshelf interactions
- collections.ts: Added Alpine for collection management
- conflicts.ts: Added Alpine for conflict resolution
- device-management.ts: Added Alpine with event delegation for dynamic content
- header.ts: Added Alpine for theme dropdown and user menu
- library.ts: Added Alpine registrations
- linking.ts: Added Alpine registrations
- queue.ts: Added Alpine for queue operations
- search.ts: Added Alpine registrations
- themeDropdown.ts: Added Alpine for theme switching

Each module now exports functions both traditionally and via Alpine.global() for template access.
This commit is contained in:
2026-03-08 21:36:24 -04:00
parent dc288e6169
commit 6728ba83a1
12 changed files with 1028 additions and 393 deletions
+97 -109
View File
@@ -1,5 +1,19 @@
// Library management functionality for admin/library page
// Procedural/imperative style (no OOP)
// Procedural/imperative style t
// nt
// OOP)
import { Alpine } from "./alpine";
import {
apiPut,
apiPost,
apiGet,
handleResponse,
handleError,
handleVoidResponse,
apiDelete,
} from "./api";
import { showToast } from "./toast";
interface Library {
id: string;
@@ -18,44 +32,22 @@ interface LibraryFolder {
created_at: string;
}
interface DeleteFolderRequest {
folder_path: string;
}
interface User {
id: string;
username: string;
email: string;
role: string;
}
interface LibrariesResponse {
data: Library[];
}
interface VisibleLibrariesResponse {
data: Array<{
id: string;
name: string;
type_name: string;
}>;
}
// State (already SSR'd, used for updates)
let libraries: Library[] = [];
let users: User[] = [];
// Reload libraries from API (called after create/delete/update)
async function reloadLibraries(): Promise<void> {
try {
const response = await (window as any).api.get("/libraries");
const result = (await (window as any).api.handleResponse(
response,
)) as LibrariesResponse;
const response = await apiGet("/libraries");
const result = handleResponse(response) as unknown as LibrariesResponse;
libraries = result.data;
renderLibraries();
} catch (error) {
(window as any).api.handleError(error, "Failed to load libraries");
handleError(error, "Failed to load libraries");
}
}
@@ -108,10 +100,10 @@ async function loadUserVisibility(): Promise<void> {
}
try {
const response = await (window as any).api.get("/libraries/visible");
const visibleLibraries = (await (window as any).api.handleResponse(
const response = await apiGet("/libraries/visible");
const visibleLibraries = (await handleResponse(
response,
)) as Library[];
)) as unknown as Library[];
const container = document.getElementById("user-libraries");
if (!container) return;
@@ -132,7 +124,7 @@ async function loadUserVisibility(): Promise<void> {
})
.join("");
} catch (error) {
(window as any).api.handleError(error, "Failed to load user libraries");
handleError(error, "Failed to load user libraries");
}
}
@@ -153,23 +145,18 @@ async function setLibraryVisibility(
);
try {
const response = await (window as any).api.post("/libraries/visibility", {
const response = await apiPost("/libraries/visibility", {
library_id: libraryId,
is_visible: isVisible,
});
await (window as any).api.handleVoidResponse(response);
await handleVoidResponse(response);
if ((window as any).showToast?.success) {
(window as any).showToast.success("Library visibility updated");
}
showToast("Library visibility updated", "success");
// Refresh visibility controls
void loadUserVisibility();
} catch (error) {
(window as any).api.handleError(
error,
"Failed to update library visibility",
);
handleError(error, "Failed to update library visibility");
}
}
@@ -192,23 +179,20 @@ async function handleCreateLibrarySubmit(event: Event): Promise<void> {
try {
const url = isEdit ? `/libraries/${libraryId}` : "/libraries";
const method = isEdit ? "put" : "post";
const response = await (window as any).api[method](url, libraryData);
const response = isEdit
? await apiPut(url, libraryData)
: await apiPost(url, libraryData);
if (isEdit) {
await (window as any).api.handleVoidResponse(response);
await handleVoidResponse(response);
} else {
(await (window as any).api.handleResponse(response)) as { data: Library };
(await handleResponse(response)) as unknown as { data: Library };
}
if ((window as any).showToast?.success) {
(window as any).showToast.success(
isEdit
? "Library updated successfully"
: "Library created successfully",
);
}
showToast(
isEdit ? "Library updated successfully" : "Library created successfully",
"success",
);
hideCreateLibraryModal();
form.reset();
@@ -222,7 +206,7 @@ async function handleCreateLibrarySubmit(event: Event): Promise<void> {
void reloadLibraries();
} catch (error) {
(window as any).api.handleError(
handleError(
error,
isEdit ? "Failed to update library" : "Failed to create library",
);
@@ -243,12 +227,10 @@ async function showLibraryFolders(libraryId: string): Promise<void> {
if (!container) return;
try {
const response = await (window as any).api.get(
`/libraries/${libraryId}/folders`,
);
const folders = (await (window as any).api.handleResponse(
const response = await apiGet(`/libraries/${libraryId}/folders`);
const folders = (await handleResponse(
response,
)) as LibraryFolder[];
)) as unknown as LibraryFolder[];
container.innerHTML = folders
.map(
@@ -273,7 +255,7 @@ async function showLibraryFolders(libraryId: string): Promise<void> {
container.classList.remove("hidden");
} catch (error) {
(window as any).api.handleError(error, "Failed to load folders");
handleError(error, "Failed to load folders");
}
}
@@ -289,24 +271,19 @@ async function addLibraryFolder(libraryId: string): Promise<void> {
}
try {
const response = await (window as any).api.post(
`/libraries/${libraryId}/folders`,
{
folder_path: folderPath,
},
);
await (window as any).api.handleVoidResponse(response);
const response = await apiPost(`/libraries/${libraryId}/folders`, {
folder_path: folderPath,
});
await handleVoidResponse(response);
if ((window as any).showToast?.success) {
(window as any).showToast.success("Folder added successfully");
}
showToast("Folder added successfully", "success");
if (input) {
input.value = "";
}
void showLibraryFolders(libraryId); // Refresh
} catch (error) {
(window as any).api.handleError(error, "Failed to add folder");
handleError(error, "Failed to add folder");
}
}
@@ -320,19 +297,16 @@ async function removeLibraryFolder(
}
try {
const response = await (window as any).api.delete(
`/libraries/${libraryId}/folders`,
{ folder_path: folderPath },
);
await (window as any).api.handleVoidResponse(response);
const response = await apiDelete(`/libraries/${libraryId}/folders`, {
folder_path: folderPath,
});
await handleVoidResponse(response);
if ((window as any).showToast?.success) {
(window as any).showToast.success("Folder removed successfully");
}
showToast("Folder removed successfully", "success");
void showLibraryFolders(libraryId); // Refresh
} catch (error) {
(window as any).api.handleError(error, "Failed to remove folder");
handleError(error, "Failed to remove folder");
}
}
@@ -340,9 +314,7 @@ async function removeLibraryFolder(
function editLibrary(libraryId: string): void {
const library = libraries.find((l) => l.id === libraryId);
if (!library) {
if ((window as any).showToast?.error) {
(window as any).showToast.error("Library not found");
}
showToast("Library not found", "error");
return;
}
@@ -439,14 +411,10 @@ async function confirmDeleteLibrary(): Promise<void> {
hideDeleteModal();
try {
const response = await (window as any).api.delete(
`/libraries/${libraryId}`,
);
await (window as any).api.handleVoidResponse(response);
const response = await apiDelete(`/libraries/${libraryId}`);
await handleVoidResponse(response);
if ((window as any).showToast?.success) {
(window as any).showToast.success("Library deleted successfully");
}
showToast("Library deleted successfully", "success");
await reloadLibraries();
@@ -457,7 +425,7 @@ async function confirmDeleteLibrary(): Promise<void> {
libraryIdInput.value = "";
}
} catch (error) {
(window as any).api.handleError(error, "Failed to delete library");
handleError(error, "Failed to delete library");
}
}
@@ -571,10 +539,10 @@ function showFolderBrowser(inputId: string): void {
// Load directories for browsing
async function loadBrowseDirectories(path: string): Promise<void> {
try {
const response = await (window as any).api.get(
const response = await apiGet(
`/libraries/browse?path=${encodeURIComponent(path)}`,
);
const data = (await (window as any).api.handleResponse(response)) as {
const data = (await handleResponse(response)) as unknown as {
current_path: string;
parent_path: string;
directories: string[];
@@ -583,7 +551,7 @@ async function loadBrowseDirectories(path: string): Promise<void> {
currentBrowsePath = data.current_path;
renderBrowseDirectories(data);
} catch (error) {
(window as any).api.handleError(error, "Failed to load directories");
handleError(error, "Failed to load directories");
}
}
@@ -686,26 +654,46 @@ function initializeLibraryAdmin(): void {
void reloadLibraries();
}
// Export functions for global access
(window as any).deleteLibrary = deleteLibrary;
(window as any).showLibraryFolders = showLibraryFolders;
(window as any).addLibraryFolder = addLibraryFolder;
(window as any).removeLibraryFolder = removeLibraryFolder;
(window as any).setLibraryVisibility = setLibraryVisibility;
(window as any).loadUserVisibility = loadUserVisibility;
(window as any).editLibrary = editLibrary;
(window as any).handleCreateLibrarySubmit = handleCreateLibrarySubmit;
(window as any).showFolderBrowser = showFolderBrowser;
(window as any).navigateFolderBrowser = navigateFolderBrowser;
(window as any).selectBrowseFolder = selectBrowseFolder;
(window as any).hideFolderBrowser = hideFolderBrowser;
(window as any).showDeleteModal = showDeleteModal;
(window as any).hideDeleteModal = hideDeleteModal;
(window as any).confirmDeleteLibrary = confirmDeleteLibrary;
// Initialize on DOM ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initializeLibraryAdmin);
} else {
initializeLibraryAdmin();
}
// Export functions for global access
export {
deleteLibrary,
showLibraryFolders,
addLibraryFolder,
removeLibraryFolder,
setLibraryVisibility,
loadUserVisibility,
editLibrary,
handleCreateLibrarySubmit,
showFolderBrowser,
navigateFolderBrowser,
selectBrowseFolder,
hideFolderBrowser,
showDeleteModal,
hideDeleteModal,
confirmDeleteLibrary,
};
Alpine.global("library", {
deleteLibrary,
showLibraryFolders,
addLibraryFolder,
removeLibraryFolder,
setLibraryVisibility,
loadUserVisibility,
editLibrary,
handleCreateLibrarySubmit,
showFolderBrowser,
navigateFolderBrowser,
selectBrowseFolder,
hideFolderBrowser,
showDeleteModal,
hideDeleteModal,
confirmDeleteLibrary,
});