diff --git a/templates/dashboard.templ b/templates/dashboard.templ index 83fd37c..df75458 100644 --- a/templates/dashboard.templ +++ b/templates/dashboard.templ @@ -100,14 +100,94 @@ templ Dashboard(user User) { + +
+
+ + + + + Filters + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + + +
@@ -370,11 +450,11 @@ templ Dashboard(user User) { function filterMedia() { const searchTerm = document.getElementById('search-input').value.toLowerCase(); - const filteredItems = mediaItems.filter(item => + const filteredItems = mediaItems.filter(item => item.title.toLowerCase().includes(searchTerm) || (item.author && item.author.toLowerCase().includes(searchTerm)) ); - + // Temporarily replace mediaItems and re-render const tempItems = mediaItems; mediaItems = filteredItems; @@ -382,15 +462,198 @@ templ Dashboard(user User) { mediaItems = tempItems; } + function applyFilters() { + const filters = { + author: document.getElementById('filter-author').value, + genre: document.getElementById('filter-genre').value, + series: document.getElementById('filter-series').value, + language: document.getElementById('filter-language').value, + yearMin: parseInt(document.getElementById('filter-year-min').value) || 0, + yearMax: parseInt(document.getElementById('filter-year-max').value) || 0, + hasCover: document.getElementById('filter-has-cover').checked + }; + + const sort = document.getElementById('sort-select').value; + + // Update URL for shareability + const params = new URLSearchParams(); + params.set('library_id', currentLibrary.id); + params.set('sort', sort); + Object.entries(filters).forEach(([key, value]) => { + if (value && value !== 0) { + if (key === 'hasCover') { + if (value === true) params.set(`has_cover`, 'true'); + } else { + params.set(`${key}_filter`, value); + } + } + }); + + window.history.pushState({}, '', `?${params.toString()}`); + + loadMediaItemsWithFilters(filters, sort); + displayActiveFilters(filters); + } + + function loadMediaItemsWithFilters(filters, sort) { + if (!currentLibrary) return; + + const loading = document.getElementById('loading'); + const container = document.getElementById('media-container'); + + loading.style.display = 'block'; + container.innerHTML = ''; + + const params = new URLSearchParams(); + params.set('library_id', currentLibrary.id); + params.set('sort', sort); + params.set('limit', 50); + params.set('offset', 0); + + // Only add filter params if they have values + if (filters.author) params.set('author_filter', filters.author); + if (filters.series) params.set('series_filter', filters.series); + if (filters.genre) params.set('genre_filter', filters.genre); + if (filters.language) params.set('language_filter', filters.language); + if (filters.yearMin) params.set('year_min', filters.yearMin); + if (filters.yearMax) params.set('year_max', filters.yearMax); + if (filters.hasCover) params.set('has_cover', 'true'); + + fetch(`/api/media-items/filtered?${params.toString()}`, { + headers: { + 'Authorization': 'Bearer ' + localStorage.getItem('token'), + 'Content-Type': 'application/json' + } + }) + .then(response => response.json()) + .then(data => { + loading.style.display = 'none'; + mediaItems = data.data || []; + renderMediaItems(); + }) + .catch(error => { + loading.style.display = 'none'; + console.error('Error loading filtered media items:', error); + container.innerHTML = '
Error loading media items. Please try again.
'; + }); + } + + function displayActiveFilters(filters) { + const container = document.getElementById('active-filters'); + const chipsContainer = document.getElementById('active-filters-container'); + chipsContainer.innerHTML = ''; + + let hasFilters = false; + + const filterLabels = { + author: 'Author', + genre: 'Genre', + series: 'Series', + language: 'Language', + yearMin: 'Year From', + yearMax: 'Year To', + hasCover: 'Has Cover' + }; + + Object.entries(filters).forEach(([key, value]) => { + if (value && value !== 0) { + hasFilters = true; + const label = filterLabels[key] || key; + const displayValue = key === 'hasCover' ? 'Yes' : value; + + const chip = document.createElement('span'); + chip.className = 'px-3 py-1 rounded-full text-sm flex items-center gap-2 cursor-pointer'; + chip.style.cssText = 'background-color: var(--accent); color: var(--bg-primary)'; + chip.innerHTML = `${label}: ${displayValue} `; + chip.onclick = () => clearFilter(key); + chipsContainer.appendChild(chip); + } + }); + + container.classList.toggle('hidden', !hasFilters); + } + + function clearFilter(filterKey) { + switch (filterKey) { + case 'author': + document.getElementById('filter-author').value = ''; + break; + case 'genre': + document.getElementById('filter-genre').value = ''; + break; + case 'series': + document.getElementById('filter-series').value = ''; + break; + case 'language': + document.getElementById('filter-language').value = ''; + break; + case 'yearMin': + document.getElementById('filter-year-min').value = ''; + break; + case 'yearMax': + document.getElementById('filter-year-max').value = ''; + break; + case 'hasCover': + document.getElementById('filter-has-cover').checked = false; + break; + } + applyFilters(); + } + + function clearFilters() { + document.getElementById('filter-author').value = ''; + document.getElementById('filter-genre').value = ''; + document.getElementById('filter-series').value = ''; + document.getElementById('filter-language').value = ''; + document.getElementById('filter-year-min').value = ''; + document.getElementById('filter-year-max').value = ''; + document.getElementById('filter-has-cover').checked = false; + + // Clear URL params + const params = new URLSearchParams(); + params.set('library_id', currentLibrary.id); + window.history.pushState({}, '', `?${params.toString()}`); + + loadMediaItems(); + document.getElementById('active-filters').classList.add('hidden'); + } + function viewMediaItem(mediaId) { // TODO: Implement detailed media view alert('Media viewer coming soon!'); } - // Load libraries on page load + // Load filters from URL on page load document.addEventListener('DOMContentLoaded', function() { loadTheme(); loadLibraries(); + + // Restore filters from URL + const params = new URLSearchParams(window.location.search); + if (params.has('author_filter')) { + document.getElementById('filter-author').value = params.get('author_filter'); + } + if (params.has('genre_filter')) { + document.getElementById('filter-genre').value = params.get('genre_filter'); + } + if (params.has('series_filter')) { + document.getElementById('filter-series').value = params.get('series_filter'); + } + if (params.has('language_filter')) { + document.getElementById('filter-language').value = params.get('language_filter'); + } + if (params.has('year_min')) { + document.getElementById('filter-year-min').value = params.get('year_min'); + } + if (params.has('year_max')) { + document.getElementById('filter-year-max').value = params.get('year_max'); + } + if (params.has('has_cover')) { + document.getElementById('filter-has-cover').checked = params.get('has_cover') === 'true'; + } + if (params.has('sort')) { + document.getElementById('sort-select').value = params.get('sort'); + } }); diff --git a/templates/dashboard_templ.go b/templates/dashboard_templ.go index 1210836..3dc9603 100644 --- a/templates/dashboard_templ.go +++ b/templates/dashboard_templ.go @@ -29,7 +29,15 @@ func Dashboard(user User) templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Dashboard - BookmannHeader(user, \"/dashboard\")

Your Media Library

Browse and manage your media collections

Loading your media libraries...
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Dashboard - Bookmann") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = Header(user, "/dashboard").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

Your Media Library

Browse and manage your media collections

Filters
Loading your media libraries...
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }