feat: add Disney/Non-Disney quick filter to dropdown panel
Backend: - disney_filter query param on /api/assets (values: 'disney' | 'non_disney') - Detects Disney by checking disney_park IS NOT NULL OR customer name LIKE 'D-%' - Catches ESPN, Wide World of Sports, all Disney resorts, etc. - Adds LEFT JOIN to customers table when disney_filter is active Frontend: - '🏰 Disney only' and '🏢 Non-Disney' options in Disney Park dropdown - Active filter tag shows which is selected - Works together with other dropdown filters
This commit is contained in:
@@ -988,6 +988,7 @@ def list_assets(
|
|||||||
location_id: Optional[int] = Query(None),
|
location_id: Optional[int] = Query(None),
|
||||||
assigned_to: Optional[int] = Query(None),
|
assigned_to: Optional[int] = Query(None),
|
||||||
disney_park: Optional[str] = Query(None),
|
disney_park: Optional[str] = Query(None),
|
||||||
|
disney_filter: Optional[str] = Query(None),
|
||||||
q: Optional[str] = Query(None),
|
q: Optional[str] = Query(None),
|
||||||
limit: int = Query(100, ge=1, le=2000),
|
limit: int = Query(100, ge=1, le=2000),
|
||||||
offset: int = Query(0, ge=0),
|
offset: int = Query(0, ge=0),
|
||||||
@@ -1008,6 +1009,10 @@ def list_assets(
|
|||||||
if disney_park:
|
if disney_park:
|
||||||
conditions.append("disney_park = ?")
|
conditions.append("disney_park = ?")
|
||||||
params.append(disney_park)
|
params.append(disney_park)
|
||||||
|
if disney_filter == "disney":
|
||||||
|
conditions.append("(disney_park IS NOT NULL OR c.name LIKE 'D-%')")
|
||||||
|
elif disney_filter == "non_disney":
|
||||||
|
conditions.append("(disney_park IS NULL AND (c.name IS NULL OR c.name NOT LIKE 'D-%'))")
|
||||||
if model:
|
if model:
|
||||||
conditions.append("model = ?")
|
conditions.append("model = ?")
|
||||||
params.append(model)
|
params.append(model)
|
||||||
@@ -1026,7 +1031,10 @@ def list_assets(
|
|||||||
params.extend([like, like, like, like])
|
params.extend([like, like, like, like])
|
||||||
|
|
||||||
where = " AND ".join(conditions)
|
where = " AND ".join(conditions)
|
||||||
sql = "SELECT * FROM assets"
|
join_clause = ""
|
||||||
|
if disney_filter:
|
||||||
|
join_clause = " LEFT JOIN customers c ON assets.customer_id = c.id"
|
||||||
|
sql = f"SELECT assets.* FROM assets{join_clause}"
|
||||||
if where:
|
if where:
|
||||||
sql += f" WHERE {where}"
|
sql += f" WHERE {where}"
|
||||||
sql += " ORDER BY created_at DESC LIMIT ? OFFSET ?"
|
sql += " ORDER BY created_at DESC LIMIT ? OFFSET ?"
|
||||||
@@ -1034,7 +1042,7 @@ def list_assets(
|
|||||||
|
|
||||||
rows = conn.execute(sql, params).fetchall()
|
rows = conn.execute(sql, params).fetchall()
|
||||||
# Get total count for pagination
|
# Get total count for pagination
|
||||||
count_sql = "SELECT COUNT(*) FROM assets"
|
count_sql = f"SELECT COUNT(*) FROM assets{join_clause}"
|
||||||
if where:
|
if where:
|
||||||
count_sql += f" WHERE {where}"
|
count_sql += f" WHERE {where}"
|
||||||
total = conn.execute(count_sql, params[:len(params)-2]).fetchone()[0]
|
total = conn.execute(count_sql, params[:len(params)-2]).fetchone()[0]
|
||||||
|
|||||||
+25
-3
@@ -1134,6 +1134,9 @@
|
|||||||
<div class="fp-label">Disney Park</div>
|
<div class="fp-label">Disney Park</div>
|
||||||
<select id="filterDisneyPark" class="input-field" onchange="onFilterChange()">
|
<select id="filterDisneyPark" class="input-field" onchange="onFilterChange()">
|
||||||
<option value="">All parks</option>
|
<option value="">All parks</option>
|
||||||
|
<option value="__disney__">🏰 Disney only</option>
|
||||||
|
<option value="__non_disney__">🏢 Non-Disney</option>
|
||||||
|
<option disabled>──────────</option>
|
||||||
<option value="magic-kingdom">🏰 Magic Kingdom</option>
|
<option value="magic-kingdom">🏰 Magic Kingdom</option>
|
||||||
<option value="epcot">🌍 Epcot</option>
|
<option value="epcot">🌍 Epcot</option>
|
||||||
<option value="hollywood-studios">🎬 Hollywood Studios</option>
|
<option value="hollywood-studios">🎬 Hollywood Studios</option>
|
||||||
@@ -3130,7 +3133,7 @@
|
|||||||
function parkName(key) { return PARK_NAMES[key] || key; }
|
function parkName(key) { return PARK_NAMES[key] || key; }
|
||||||
|
|
||||||
// ── Filter state ──────────────────────────────────────────────────────
|
// ── Filter state ──────────────────────────────────────────────────────
|
||||||
let assetFilters = { category: null, status: null, make: null, disney_park: null, q: '',
|
let assetFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
|
||||||
customer_id: null, location_id: null, assigned_to: null };
|
customer_id: null, location_id: null, assigned_to: null };
|
||||||
let assetOffset = 0;
|
let assetOffset = 0;
|
||||||
const ASSET_PAGE_SIZE = 2000;
|
const ASSET_PAGE_SIZE = 2000;
|
||||||
@@ -3208,7 +3211,18 @@
|
|||||||
|
|
||||||
assetFilters.category = catEl.value || null;
|
assetFilters.category = catEl.value || null;
|
||||||
assetFilters.make = makeEl.value || null;
|
assetFilters.make = makeEl.value || null;
|
||||||
assetFilters.disney_park = parkEl.value || null;
|
// Handle Disney/Non-Disney special options
|
||||||
|
var parkVal = parkEl.value;
|
||||||
|
if (parkVal === '__disney__') {
|
||||||
|
assetFilters.disney_park = null;
|
||||||
|
assetFilters.disney_filter = 'disney';
|
||||||
|
} else if (parkVal === '__non_disney__') {
|
||||||
|
assetFilters.disney_park = null;
|
||||||
|
assetFilters.disney_filter = 'non_disney';
|
||||||
|
} else {
|
||||||
|
assetFilters.disney_park = parkVal || null;
|
||||||
|
assetFilters.disney_filter = null;
|
||||||
|
}
|
||||||
assetFilters.customer_id = custEl.value || null;
|
assetFilters.customer_id = custEl.value || null;
|
||||||
assetFilters.status = stEl.value || null;
|
assetFilters.status = stEl.value || null;
|
||||||
assetFilters.assigned_to = assnEl.value || null;
|
assetFilters.assigned_to = assnEl.value || null;
|
||||||
@@ -3247,6 +3261,7 @@
|
|||||||
if (assetFilters.status) count++;
|
if (assetFilters.status) count++;
|
||||||
if (assetFilters.make) count++;
|
if (assetFilters.make) count++;
|
||||||
if (assetFilters.disney_park) count++;
|
if (assetFilters.disney_park) count++;
|
||||||
|
if (assetFilters.disney_filter) count++;
|
||||||
if (assetFilters.customer_id) count++;
|
if (assetFilters.customer_id) count++;
|
||||||
if (assetFilters.location_id) count++;
|
if (assetFilters.location_id) count++;
|
||||||
if (assetFilters.assigned_to) count++;
|
if (assetFilters.assigned_to) count++;
|
||||||
@@ -3270,6 +3285,8 @@
|
|||||||
if (assetFilters.status) tags.push({key:'status', label: statusIcon(assetFilters.status) + ' ' + assetFilters.status});
|
if (assetFilters.status) tags.push({key:'status', label: statusIcon(assetFilters.status) + ' ' + assetFilters.status});
|
||||||
if (assetFilters.make) tags.push({key:'make', label:'\uD83C\uDFED ' + assetFilters.make});
|
if (assetFilters.make) tags.push({key:'make', label:'\uD83C\uDFED ' + assetFilters.make});
|
||||||
if (assetFilters.disney_park) tags.push({key:'disney_park', label: parkIcon(assetFilters.disney_park) + ' ' + parkName(assetFilters.disney_park)});
|
if (assetFilters.disney_park) tags.push({key:'disney_park', label: parkIcon(assetFilters.disney_park) + ' ' + parkName(assetFilters.disney_park)});
|
||||||
|
if (assetFilters.disney_filter === 'disney') tags.push({key:'disney_filter', label:'🏰 Disney only'});
|
||||||
|
if (assetFilters.disney_filter === 'non_disney') tags.push({key:'disney_filter', label:'🏢 Non-Disney'});
|
||||||
if (assetFilters.customer_id) {
|
if (assetFilters.customer_id) {
|
||||||
var custSel = document.getElementById('filterCustomer');
|
var custSel = document.getElementById('filterCustomer');
|
||||||
var txt = custSel.options[custSel.selectedIndex] ? custSel.options[custSel.selectedIndex].text : 'Customer';
|
var txt = custSel.options[custSel.selectedIndex] ? custSel.options[custSel.selectedIndex].text : 'Customer';
|
||||||
@@ -3311,6 +3328,9 @@
|
|||||||
} else if (key === 'disney_park') {
|
} else if (key === 'disney_park') {
|
||||||
document.getElementById('filterDisneyPark').value = '';
|
document.getElementById('filterDisneyPark').value = '';
|
||||||
assetFilters.disney_park = null;
|
assetFilters.disney_park = null;
|
||||||
|
} else if (key === 'disney_filter') {
|
||||||
|
document.getElementById('filterDisneyPark').value = '';
|
||||||
|
assetFilters.disney_filter = null;
|
||||||
} else if (key === 'customer_id') {
|
} else if (key === 'customer_id') {
|
||||||
document.getElementById('filterCustomer').value = '';
|
document.getElementById('filterCustomer').value = '';
|
||||||
document.getElementById('filterLocation').innerHTML = '<option value="">All locations</option>';
|
document.getElementById('filterLocation').innerHTML = '<option value="">All locations</option>';
|
||||||
@@ -3330,7 +3350,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clearAllFilters() {
|
function clearAllFilters() {
|
||||||
assetFilters = { category: null, status: null, make: null, disney_park: null, q: '',
|
assetFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
|
||||||
customer_id: null, location_id: null, assigned_to: null };
|
customer_id: null, location_id: null, assigned_to: null };
|
||||||
document.getElementById('assetSearch').value = '';
|
document.getElementById('assetSearch').value = '';
|
||||||
document.getElementById('clearSearch').style.display = 'none';
|
document.getElementById('clearSearch').style.display = 'none';
|
||||||
@@ -3355,6 +3375,7 @@
|
|||||||
if (assetFilters.status) params.set('status', assetFilters.status);
|
if (assetFilters.status) params.set('status', assetFilters.status);
|
||||||
if (assetFilters.make) params.set('make', assetFilters.make);
|
if (assetFilters.make) params.set('make', assetFilters.make);
|
||||||
if (assetFilters.disney_park) params.set('disney_park', assetFilters.disney_park);
|
if (assetFilters.disney_park) params.set('disney_park', assetFilters.disney_park);
|
||||||
|
if (assetFilters.disney_filter) params.set('disney_filter', assetFilters.disney_filter);
|
||||||
if (assetFilters.q) params.set('q', assetFilters.q);
|
if (assetFilters.q) params.set('q', assetFilters.q);
|
||||||
if (assetFilters.customer_id) params.set('customer_id', assetFilters.customer_id);
|
if (assetFilters.customer_id) params.set('customer_id', assetFilters.customer_id);
|
||||||
if (assetFilters.location_id) params.set('location_id', assetFilters.location_id);
|
if (assetFilters.location_id) params.set('location_id', assetFilters.location_id);
|
||||||
@@ -3408,6 +3429,7 @@
|
|||||||
assetFilters.status = status;
|
assetFilters.status = status;
|
||||||
assetFilters.make = make;
|
assetFilters.make = make;
|
||||||
assetFilters.disney_park = disney_park;
|
assetFilters.disney_park = disney_park;
|
||||||
|
assetFilters.disney_filter = null;
|
||||||
// Sync dropdowns
|
// Sync dropdowns
|
||||||
if (document.getElementById('filterCategory')) document.getElementById('filterCategory').value = category || '';
|
if (document.getElementById('filterCategory')) document.getElementById('filterCategory').value = category || '';
|
||||||
if (document.getElementById('filterMake')) document.getElementById('filterMake').value = make || '';
|
if (document.getElementById('filterMake')) document.getElementById('filterMake').value = make || '';
|
||||||
|
|||||||
Reference in New Issue
Block a user