fix(ui): custom confirm dialog, picker visibility, remove wiring
Three fixes for the collection detail page: 1. Picker modal never showed because the outer overlay div had style="display:none" with no x-show binding — add x-show bound to $store.bookPicker.isOpen plus a backdrop and click-to-close. 2. Replace native confirm() with an in-page Alpine modal for UI continuity. Add confirm dialog state (showConfirm, confirmMessage, pendingAction) and methods (requestRemoveBook, requestBulkRemove, executeConfirmed, closeConfirm) to the collections component. The actual API calls (doRemoveBook/doBulkRemove) are triggered only when the user confirms. 3. Rename removeBook → requestRemoveBook and bulkRemove → requestBulkRemove in template + JS renderer so the dialog opens instead of navigating or failing silently.
This commit is contained in:
+36
-21
@@ -159,7 +159,7 @@ function renderCollectionBooks(books: BookInfo[]): void {
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 pt-3 border-t" style="border-color: var(--border);">
|
||||
<button @click="removeBook('${book.media_item_id}')" class="btn btn-secondary w-full">
|
||||
<button @click="requestRemoveBook('${book.media_item_id}')" class="btn btn-secondary w-full">
|
||||
<svg class="h-4 w-4 inline" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6l-2 14a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2L5 6"></path><path d="M10 11v6"></path><path d="M14 11v6"></path></svg>
|
||||
Remove from Collection
|
||||
</button>
|
||||
@@ -577,8 +577,7 @@ function toggleSelection(this: any, id: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
async function removeBook(id: string): Promise<void> {
|
||||
if (!confirm("Remove this book from the collection?")) return;
|
||||
async function doRemoveBook(id: string): Promise<void> {
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
try {
|
||||
@@ -600,14 +599,7 @@ async function removeBook(id: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function bulkRemove(this: any): Promise<void> {
|
||||
if (this.selectedBooks.length === 0) return;
|
||||
if (
|
||||
!confirm(
|
||||
`Remove ${this.selectedBooks.length} book(s) from this collection?`,
|
||||
)
|
||||
)
|
||||
return;
|
||||
async function doBulkRemove(ids: string[]): Promise<void> {
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
try {
|
||||
@@ -619,15 +611,11 @@ async function bulkRemove(this: any): Promise<void> {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({ book_ids: this.selectedBooks }),
|
||||
body: JSON.stringify({ book_ids: ids }),
|
||||
},
|
||||
);
|
||||
if (response.ok) {
|
||||
showToast(
|
||||
`Removed ${this.selectedBooks.length} book(s)`,
|
||||
"success",
|
||||
);
|
||||
this.selectedBooks = [];
|
||||
showToast(`Removed ${ids.length} book(s)`, "success");
|
||||
setTimeout(() => location.reload(), 500);
|
||||
} else {
|
||||
showToast("Failed to remove books", "error");
|
||||
@@ -655,7 +643,6 @@ function filterCollectionBooks(): void {
|
||||
}
|
||||
|
||||
export {
|
||||
bulkRemove,
|
||||
closeCollectionModal,
|
||||
createRule,
|
||||
deleteRule,
|
||||
@@ -668,7 +655,6 @@ export {
|
||||
loadCollections,
|
||||
navigateToCollection,
|
||||
populateIconGrid,
|
||||
removeBook,
|
||||
selectColor,
|
||||
selectIcon,
|
||||
setupHTMXAuth,
|
||||
@@ -691,7 +677,6 @@ Alpine.data("collections", () => ({
|
||||
loadCollections,
|
||||
navigateToCollection,
|
||||
populateIconGrid,
|
||||
removeBook,
|
||||
selectColor,
|
||||
selectIcon,
|
||||
selectedBooks: [] as string[],
|
||||
@@ -699,5 +684,35 @@ Alpine.data("collections", () => ({
|
||||
showAllIcons,
|
||||
testRule,
|
||||
toggleSelection,
|
||||
bulkRemove,
|
||||
|
||||
showConfirm: false,
|
||||
confirmMessage: "",
|
||||
confirmLabel: "Remove",
|
||||
pendingAction: null as null | (() => void),
|
||||
|
||||
requestRemoveBook(this: any, id: string) {
|
||||
this.confirmMessage = "Remove this book from the collection?";
|
||||
this.pendingAction = () => doRemoveBook(id);
|
||||
this.showConfirm = true;
|
||||
},
|
||||
|
||||
requestBulkRemove(this: any) {
|
||||
if (this.selectedBooks.length === 0) return;
|
||||
this.confirmMessage = `Remove ${this.selectedBooks.length} book(s) from this collection?`;
|
||||
const ids = [...this.selectedBooks];
|
||||
this.pendingAction = () => doBulkRemove(ids);
|
||||
this.showConfirm = true;
|
||||
},
|
||||
|
||||
executeConfirmed(this: any) {
|
||||
this.showConfirm = false;
|
||||
const action = this.pendingAction;
|
||||
this.pendingAction = null;
|
||||
if (action) action();
|
||||
},
|
||||
|
||||
closeConfirm(this: any) {
|
||||
this.showConfirm = false;
|
||||
this.pendingAction = null;
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user