diff --git a/server.py b/server.py
index 7aa990e..00f6e34 100644
--- a/server.py
+++ b/server.py
@@ -837,6 +837,26 @@ def list_users():
return [dict(r) for r in rows]
+@app.get("/api/categories")
+def list_categories():
+ conn = get_db()
+ rows = conn.execute(
+ "SELECT DISTINCT category FROM assets WHERE category IS NOT NULL AND category != '' ORDER BY category"
+ ).fetchall()
+ conn.close()
+ return [r["category"] for r in rows]
+
+
+@app.get("/api/makes")
+def list_makes():
+ conn = get_db()
+ rows = conn.execute(
+ "SELECT DISTINCT make FROM assets WHERE make IS NOT NULL AND make != '' ORDER BY make"
+ ).fetchall()
+ conn.close()
+ return [r["make"] for r in rows]
+
+
# ─── Task 4: POST /api/assets ──────────────────────────────────────────────
diff --git a/static/index.html b/static/index.html
index e377f8d..37dbb08 100644
--- a/static/index.html
+++ b/static/index.html
@@ -1116,6 +1116,45 @@
+
+
+
Category
+
+ All categories
+
+
+
+
+
+
+
Disney Park
+
+ All parks
+ 🏰 Magic Kingdom
+ 🌍 Epcot
+ 🎬 Hollywood Studios
+ 🌿 Animal Kingdom
+ 🛍️ Disney Springs
+ 🏨 Resort
+ 🏢 Office
+ 📍 Other
+
+
+
+
Status
+
+ All statuses
+ 🟢 Active
+ 🟡 Maintenance
+ 🔴 Retired
+
+
+
Customer
@@ -1131,21 +1170,13 @@
-
-
Status
-
- All statuses
- 🟢 Active
- 🟡 Maintenance
- 🔴 Retired
-
-
+
✕ Clear all filters
@@ -3123,17 +3154,27 @@
if (filterDropdownsLoaded) return;
filterDropdownsLoaded = true;
try {
+ // Load categories
+ var cats = await api('/api/categories');
+ var catSel = document.getElementById('filterCategory');
+ catSel.innerHTML = 'All categories ' +
+ cats.map(function(c) { return '' + esc(c) + ' '; }).join('');
+ // Load makes
+ var makes = await api('/api/makes');
+ var makeSel = document.getElementById('filterMake');
+ makeSel.innerHTML = 'All makes ' +
+ makes.map(function(m) { return '' + esc(m) + ' '; }).join('');
// Load customers
- const customers = await api('/api/customers');
- const custSel = document.getElementById('filterCustomer');
+ var customers = await api('/api/customers');
+ var custSel = document.getElementById('filterCustomer');
custSel.innerHTML = 'All customers ' +
- customers.map(c => `${esc(c.name)} `).join('');
+ customers.map(function(c) { return '' + esc(c.name) + ' '; }).join('');
// Load users for assigned_to filter
- const users = await api('/api/users');
- const assnSel = document.getElementById('filterAssignedTo');
+ var users = await api('/api/users');
+ var assnSel = document.getElementById('filterAssignedTo');
assnSel.innerHTML = 'Anyone ' +
- users.filter(u => u.role === 'technician' || u.role === 'admin')
- .map(u => `${esc(u.username)} `).join('');
+ users.filter(function(u) { return u.role === 'technician' || u.role === 'admin'; })
+ .map(function(u) { return '' + esc(u.username) + ' '; }).join('');
} catch (e) {
// silent fail — dropdowns just show defaults
}
@@ -3158,11 +3199,17 @@
}
function onFilterChange() {
+ var catEl = document.getElementById('filterCategory');
+ var makeEl = document.getElementById('filterMake');
+ var parkEl = document.getElementById('filterDisneyPark');
var custEl = document.getElementById('filterCustomer');
var locEl = document.getElementById('filterLocation');
var stEl = document.getElementById('filterStatus');
var assnEl = document.getElementById('filterAssignedTo');
+ assetFilters.category = catEl.value || null;
+ assetFilters.make = makeEl.value || null;
+ assetFilters.disney_park = parkEl.value || null;
assetFilters.customer_id = custEl.value || null;
assetFilters.status = stEl.value || null;
assetFilters.assigned_to = assnEl.value || null;
@@ -3254,13 +3301,16 @@
assetFilters.q = '';
document.getElementById('clearSearch').style.display = 'none';
} else if (key === 'category') {
+ document.getElementById('filterCategory').value = '';
assetFilters.category = null;
} else if (key === 'status') {
document.getElementById('filterStatus').value = '';
assetFilters.status = null;
} else if (key === 'make') {
+ document.getElementById('filterMake').value = '';
assetFilters.make = null;
} else if (key === 'disney_park') {
+ document.getElementById('filterDisneyPark').value = '';
assetFilters.disney_park = null;
} else if (key === 'customer_id') {
document.getElementById('filterCustomer').value = '';
@@ -3285,6 +3335,9 @@
customer_id: null, location_id: null, assigned_to: null };
document.getElementById('assetSearch').value = '';
document.getElementById('clearSearch').style.display = 'none';
+ document.getElementById('filterCategory').value = '';
+ document.getElementById('filterMake').value = '';
+ document.getElementById('filterDisneyPark').value = '';
document.getElementById('filterCustomer').value = '';
document.getElementById('filterLocation').innerHTML = 'All locations ';
document.getElementById('filterLocation').disabled = true;
@@ -3392,14 +3445,14 @@
assetFilters.status = status;
assetFilters.make = make;
assetFilters.disney_park = disney_park;
+ // Sync dropdowns
+ if (document.getElementById('filterCategory')) document.getElementById('filterCategory').value = category || '';
+ if (document.getElementById('filterMake')) document.getElementById('filterMake').value = make || '';
+ if (document.getElementById('filterDisneyPark')) document.getElementById('filterDisneyPark').value = disney_park || '';
+ if (document.getElementById('filterStatus')) document.getElementById('filterStatus').value = status || '';
+ renderActiveFilterTags();
assetOffset = 0;
- loadAssets();
- }
-
- function clearAssetSearch() {
- document.getElementById('assetSearch').value = '';
- assetOffset = 0;
- loadAssets();
+ _loadAssets();
}
// ── Pagination ──────────────────────────────────────────────────────────