feat: add autocomplete dropdown support for filter fields
- Add fetchFieldValues helper function for API calls
- Add fetchAuthorValues for author autocomplete
- Add fetchGenreValues for genre autocomplete
- Add fetchSeriesValues for series autocomplete
- Add fetchLanguageValues for language autocomplete
- Functions use native DOM manipulation to populate datalist elements
- No Alpine.js reactive state (simple pattern, not reactive)
- Functions registered as methods in Alpine.data("bookshelf") component
- Triggers on input with 300ms debounce after 2 characters minimum
- Updates include count in option text (e.g., "Asimov, Isaac (47)")
Uses /api/media-items/search with field-specific params (author=value, genre=value, etc.).
This commit is contained in:
@@ -227,6 +227,87 @@ Alpine.data("bookshelf", () => ({
|
||||
showToast("Error saving filter", "error");
|
||||
}
|
||||
},
|
||||
|
||||
// Autocomplete helper function
|
||||
async fetchFieldValues(
|
||||
field: string,
|
||||
search: string,
|
||||
datalistId: string,
|
||||
): Promise<void> {
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) {
|
||||
console.error("Not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
if (search.length < 2) return;
|
||||
|
||||
const currentLibraryId = (
|
||||
document.getElementById("library-select") as HTMLSelectElement
|
||||
)?.value;
|
||||
if (!currentLibraryId) {
|
||||
console.error("No library selected");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/media-items/search?${field}=${encodeURIComponent(
|
||||
search,
|
||||
)}&library_id=${currentLibraryId}&limit=50`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Failed to fetch field values");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Update datalist via DOM manipulation
|
||||
const datalist = document.getElementById(datalistId);
|
||||
if (!datalist) {
|
||||
console.error(`Datalist ${datalistId} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing options
|
||||
datalist.innerHTML = "";
|
||||
|
||||
// Add new options
|
||||
data.results.forEach((item: { value: string; count: number }) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = item.value;
|
||||
option.textContent = `${item.value} (${item.count})`;
|
||||
datalist.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching field values:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// Fetch author values for autocomplete
|
||||
async fetchAuthorValues(input: HTMLInputElement): Promise<void> {
|
||||
await this.fetchFieldValues("author", input.value, "author-datalist");
|
||||
},
|
||||
|
||||
// Fetch genre values for autocomplete
|
||||
async fetchGenreValues(input: HTMLInputElement): Promise<void> {
|
||||
await this.fetchFieldValues("genre", input.value, "genre-datalist");
|
||||
},
|
||||
|
||||
// Fetch series values for autocomplete
|
||||
async fetchSeriesValues(input: HTMLInputElement): Promise<void> {
|
||||
await this.fetchFieldValues("series", input.value, "series-datalist");
|
||||
},
|
||||
|
||||
// Fetch language values for autocomplete
|
||||
async fetchLanguageValues(input: HTMLInputElement): Promise<void> {
|
||||
await this.fetchFieldValues("language", input.value, "language-datalist");
|
||||
},
|
||||
}));
|
||||
|
||||
// Standalone function - initialize the bookshelf (NOT async, like dashboard.ts)
|
||||
|
||||
Reference in New Issue
Block a user