Add disney_group grouped park filter categories
- New disney_group query param on /api/assets (park, resort, office, springs, other) - Both list and map filter dropdowns show group options above individual parks - Backend uses IN queries for grouped park values - Map view handles groups via client-side PARK_GROUP_MAP Closes #69
This commit is contained in:
@@ -1065,6 +1065,7 @@ def list_assets(
|
||||
assigned_to: Optional[int] = Query(None),
|
||||
disney_park: Optional[str] = Query(None),
|
||||
disney_filter: Optional[str] = Query(None),
|
||||
disney_group: Optional[str] = Query(None, description="Grouped park categories: park, resort, office, springs, other"),
|
||||
q: Optional[str] = Query(None),
|
||||
no_dex_days: Optional[int] = Query(None, ge=1),
|
||||
has_gps: Optional[bool] = Query(None),
|
||||
@@ -1092,6 +1093,19 @@ def list_assets(
|
||||
conditions.append("is_disney = 1")
|
||||
elif disney_filter == "non_disney":
|
||||
conditions.append("(is_disney IS NULL OR is_disney = 0)")
|
||||
if disney_group:
|
||||
park_groups = {
|
||||
"park": ["magic-kingdom", "epcot", "hollywood-studios", "animal-kingdom"],
|
||||
"resort": ["resort"],
|
||||
"office": ["office"],
|
||||
"springs": ["disney-springs"],
|
||||
"other": ["other"],
|
||||
}
|
||||
parks = park_groups.get(disney_group)
|
||||
if parks:
|
||||
placeholders = ",".join("?" * len(parks))
|
||||
conditions.append(f"disney_park IN ({placeholders})")
|
||||
params.extend(parks)
|
||||
if model:
|
||||
conditions.append("model = ?")
|
||||
params.append(model)
|
||||
|
||||
+73
-11
@@ -1340,7 +1340,13 @@
|
||||
<div class="fp-label">Disney Park</div>
|
||||
<select id="filterDisneyPark" class="input-field" onchange="onFilterChange()">
|
||||
<option value="">All parks</option>
|
||||
<option disabled>──────────</option>
|
||||
<option disabled>──── Groups ────</option>
|
||||
<option value="group:park">🏰 Disney Park (all parks)</option>
|
||||
<option value="group:resort">🏨 Disney Resort</option>
|
||||
<option value="group:office">🏢 Disney Office</option>
|
||||
<option value="group:springs">🛍️ Disney Springs</option>
|
||||
<option value="group:other">📍 Other Disney</option>
|
||||
<option disabled>── Individual ──</option>
|
||||
<option value="magic-kingdom">🏰 Magic Kingdom</option>
|
||||
<option value="epcot">🌍 Epcot</option>
|
||||
<option value="hollywood-studios">🎬 Hollywood Studios</option>
|
||||
@@ -1550,7 +1556,13 @@
|
||||
<div class="fp-label">Disney Park</div>
|
||||
<select id="mapFilterDisneyPark" class="input-field" onchange="onMapFilterChange()">
|
||||
<option value="">All parks</option>
|
||||
<option disabled>──────────</option>
|
||||
<option disabled>──── Groups ────</option>
|
||||
<option value="group:park">🏰 Disney Park (all parks)</option>
|
||||
<option value="group:resort">🏨 Disney Resort</option>
|
||||
<option value="group:office">🏢 Disney Office</option>
|
||||
<option value="group:springs">🛍️ Disney Springs</option>
|
||||
<option value="group:other">📍 Other Disney</option>
|
||||
<option disabled>── Individual ──</option>
|
||||
<option value="magic-kingdom">🏰 Magic Kingdom</option>
|
||||
<option value="epcot">🌍 Epcot</option>
|
||||
<option value="hollywood-studios">🎬 Hollywood Studios</option>
|
||||
@@ -3899,6 +3911,15 @@
|
||||
|
||||
const PARK_ICONS = {'magic-kingdom':'🏰','epcot':'🌍','hollywood-studios':'🎬','animal-kingdom':'🌿','disney-springs':'🛍️','resort':'🏨','office':'🏢','other':'📍'};
|
||||
const PARK_NAMES = {'magic-kingdom':'Magic Kingdom','epcot':'Epcot','hollywood-studios':'Hollywood Studios','animal-kingdom':'Animal Kingdom','disney-springs':'Disney Springs','resort':'Resort','office':'Office','other':'Other'};
|
||||
const PARK_GROUP_ICONS = {'park':'🏰','resort':'🏨','office':'🏢','springs':'🛍️','other':'📍'};
|
||||
const PARK_GROUP_NAMES = {'park':'Disney Park','resort':'Disney Resort','office':'Disney Office','springs':'Disney Springs','other':'Other Disney'};
|
||||
const PARK_GROUP_MAP = {
|
||||
'park': ['magic-kingdom','epcot','hollywood-studios','animal-kingdom'],
|
||||
'resort': ['resort'],
|
||||
'office': ['office'],
|
||||
'springs': ['disney-springs'],
|
||||
'other': ['other'],
|
||||
};
|
||||
function catLabel(cat) {
|
||||
const e = CATEGORY_MAP[cat];
|
||||
return e && e.label ? e.label : (cat || 'Other');
|
||||
@@ -4124,7 +4145,7 @@
|
||||
}
|
||||
|
||||
// ── Filter state ──────────────────────────────────────────────────────
|
||||
let assetFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
|
||||
let assetFilters = { category: null, status: null, make: null, disney_park: null, disney_group: null, disney_filter: null, q: '',
|
||||
customer_id: null, location_id: null, assigned_to: null, branch: null };
|
||||
let noDexFilterActive = false;
|
||||
let assetOffset = 0;
|
||||
@@ -4222,10 +4243,23 @@
|
||||
if (disneyFilterVal) {
|
||||
assetFilters.disney_filter = disneyFilterVal;
|
||||
assetFilters.disney_park = null;
|
||||
assetFilters.disney_group = null;
|
||||
} else {
|
||||
var parkVal = parkEl.value;
|
||||
assetFilters.disney_park = parkVal || null;
|
||||
if (parkVal && parkVal.startsWith('group:')) {
|
||||
// Grouped park category
|
||||
assetFilters.disney_group = parkVal.replace('group:', '');
|
||||
assetFilters.disney_park = null;
|
||||
assetFilters.disney_filter = null;
|
||||
} else if (parkVal) {
|
||||
assetFilters.disney_park = parkVal;
|
||||
assetFilters.disney_group = null;
|
||||
assetFilters.disney_filter = null;
|
||||
} else {
|
||||
assetFilters.disney_park = null;
|
||||
assetFilters.disney_group = null;
|
||||
assetFilters.disney_filter = null;
|
||||
}
|
||||
}
|
||||
assetFilters.customer_id = custEl.value || null;
|
||||
|
||||
@@ -4266,6 +4300,7 @@
|
||||
|
||||
if (assetFilters.make) count++;
|
||||
if (assetFilters.disney_park) count++;
|
||||
if (assetFilters.disney_group) count++;
|
||||
if (assetFilters.disney_filter) count++;
|
||||
if (assetFilters.customer_id) count++;
|
||||
if (assetFilters.location_id) count++;
|
||||
@@ -4292,6 +4327,7 @@
|
||||
|
||||
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_group) tags.push({key:'disney_group', label: (PARK_GROUP_ICONS[assetFilters.disney_group] || '📍') + ' ' + (PARK_GROUP_NAMES[assetFilters.disney_group] || assetFilters.disney_group)});
|
||||
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) {
|
||||
@@ -4334,6 +4370,9 @@
|
||||
} else if (key === 'disney_park') {
|
||||
document.getElementById('filterDisneyPark').value = '';
|
||||
assetFilters.disney_park = null;
|
||||
} else if (key === 'disney_group') {
|
||||
document.getElementById('filterDisneyPark').value = '';
|
||||
assetFilters.disney_group = null;
|
||||
} else if (key === 'disney_filter') {
|
||||
document.getElementById('filterDisneyFilter').value = '';
|
||||
assetFilters.disney_filter = null;
|
||||
@@ -4363,7 +4402,7 @@
|
||||
}
|
||||
|
||||
function clearAllFilters() {
|
||||
assetFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
|
||||
assetFilters = { category: null, status: null, make: null, disney_park: null, disney_group: null, disney_filter: null, q: '',
|
||||
customer_id: null, location_id: null, assigned_to: null, branch: null };
|
||||
document.getElementById('assetSearch').value = '';
|
||||
document.getElementById('clearSearch').style.display = 'none';
|
||||
@@ -5001,6 +5040,7 @@
|
||||
|
||||
if (assetFilters.make) params.set('make', assetFilters.make);
|
||||
if (assetFilters.disney_park) params.set('disney_park', assetFilters.disney_park);
|
||||
if (assetFilters.disney_group) params.set('disney_group', assetFilters.disney_group);
|
||||
if (assetFilters.disney_filter) params.set('disney_filter', assetFilters.disney_filter);
|
||||
if (assetFilters.q) params.set('q', assetFilters.q);
|
||||
if (assetFilters.customer_id) params.set('customer_id', assetFilters.customer_id);
|
||||
@@ -5069,16 +5109,17 @@
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
function setFilter(category, status, make, disney_park) {
|
||||
function setFilter(category, status, make, disney_park, disney_group) {
|
||||
assetFilters.category = category;
|
||||
assetFilters.status = status;
|
||||
assetFilters.make = make;
|
||||
assetFilters.disney_park = disney_park;
|
||||
assetFilters.disney_group = disney_group || null;
|
||||
assetFilters.disney_filter = null;
|
||||
// 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('filterDisneyPark')) document.getElementById('filterDisneyPark').value = (disney_group ? 'group:' + disney_group : (disney_park || ''));
|
||||
|
||||
renderActiveFilterTags();
|
||||
assetOffset = 0;
|
||||
@@ -6069,7 +6110,7 @@
|
||||
}
|
||||
|
||||
// ── Map filter state ──────────────────────────────────────────────────
|
||||
let mapFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
|
||||
let mapFilters = { category: null, status: null, make: null, disney_park: null, disney_group: null, disney_filter: null, q: '',
|
||||
customer_id: null, location_id: null, assigned_to: null, branch: null };
|
||||
let mapFilterDropdownsLoaded = false;
|
||||
let _mapFilterTimer;
|
||||
@@ -6128,10 +6169,22 @@
|
||||
if (disneyFilterVal) {
|
||||
mapFilters.disney_filter = disneyFilterVal;
|
||||
mapFilters.disney_park = null;
|
||||
mapFilters.disney_group = null;
|
||||
} else {
|
||||
var parkVal = document.getElementById('mapFilterDisneyPark').value;
|
||||
mapFilters.disney_park = parkVal || null;
|
||||
if (parkVal && parkVal.startsWith('group:')) {
|
||||
mapFilters.disney_group = parkVal.replace('group:', '');
|
||||
mapFilters.disney_park = null;
|
||||
mapFilters.disney_filter = null;
|
||||
} else if (parkVal) {
|
||||
mapFilters.disney_park = parkVal;
|
||||
mapFilters.disney_group = null;
|
||||
mapFilters.disney_filter = null;
|
||||
} else {
|
||||
mapFilters.disney_park = null;
|
||||
mapFilters.disney_group = null;
|
||||
mapFilters.disney_filter = null;
|
||||
}
|
||||
}
|
||||
mapFilters.customer_id = document.getElementById('mapFilterCustomer').value || null;
|
||||
|
||||
@@ -6172,6 +6225,7 @@
|
||||
if (mapFilters.category) count++;
|
||||
if (mapFilters.make) count++;
|
||||
if (mapFilters.disney_park) count++;
|
||||
if (mapFilters.disney_group) count++;
|
||||
if (mapFilters.disney_filter) count++;
|
||||
if (mapFilters.customer_id) count++;
|
||||
if (mapFilters.location_id) count++;
|
||||
@@ -6196,6 +6250,7 @@
|
||||
if (mapFilters.category) tags.push({key:'category', label:'📂 ' + mapFilters.category});
|
||||
if (mapFilters.make) tags.push({key:'make', label:'🏭 ' + mapFilters.make});
|
||||
if (mapFilters.disney_park) tags.push({key:'disney_park', label: parkIcon(mapFilters.disney_park) + ' ' + parkName(mapFilters.disney_park)});
|
||||
if (mapFilters.disney_group) tags.push({key:'disney_group', label: (PARK_GROUP_ICONS[mapFilters.disney_group] || '📍') + ' ' + (PARK_GROUP_NAMES[mapFilters.disney_group] || mapFilters.disney_group)});
|
||||
if (mapFilters.disney_filter === 'disney') tags.push({key:'disney_filter', label:'🏰 Disney only'});
|
||||
if (mapFilters.disney_filter === 'non_disney') tags.push({key:'disney_filter', label:'🏢 Non-Disney'});
|
||||
if (mapFilters.customer_id) {
|
||||
@@ -6229,6 +6284,9 @@
|
||||
} else if (key === 'disney_park') {
|
||||
document.getElementById('mapFilterDisneyPark').value = '';
|
||||
mapFilters.disney_park = null;
|
||||
} else if (key === 'disney_group') {
|
||||
document.getElementById('mapFilterDisneyPark').value = '';
|
||||
mapFilters.disney_group = null;
|
||||
} else if (key === 'disney_filter') {
|
||||
document.getElementById('mapFilterDisneyFilter').value = '';
|
||||
mapFilters.disney_filter = null;
|
||||
@@ -6253,7 +6311,7 @@
|
||||
}
|
||||
|
||||
function clearMapFilters() {
|
||||
mapFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
|
||||
mapFilters = { category: null, status: null, make: null, disney_park: null, disney_group: null, disney_filter: null, q: '',
|
||||
customer_id: null, location_id: null, assigned_to: null, branch: null };
|
||||
document.getElementById('mapSearchInput').value = '';
|
||||
document.getElementById('mapFilterCategory').value = '';
|
||||
@@ -6310,7 +6368,11 @@
|
||||
// Disney/non-disney filter
|
||||
if (mapFilters.disney_filter === 'disney' && !a.is_disney) return;
|
||||
if (mapFilters.disney_filter === 'non_disney' && a.is_disney) return;
|
||||
// Disney park filter
|
||||
// Disney park filter (individual or group)
|
||||
if (mapFilters.disney_group) {
|
||||
var groupParks = PARK_GROUP_MAP[mapFilters.disney_group] || [];
|
||||
if (groupParks.indexOf(a.disney_park) === -1) return;
|
||||
}
|
||||
if (mapFilters.disney_park && a.disney_park !== mapFilters.disney_park) return;
|
||||
// Text search
|
||||
if (q) {
|
||||
|
||||
Reference in New Issue
Block a user