diff --git a/src/ims/element/incident/incidents_template/template.xhtml b/src/ims/element/incident/incidents_template/template.xhtml index 34b49d6db..1f23bcfd9 100644 --- a/src/ims/element/incident/incidents_template/template.xhtml +++ b/src/ims/element/incident/incidents_template/template.xhtml @@ -60,14 +60,14 @@ All @@ -82,19 +82,19 @@ All Days @@ -104,15 +104,12 @@ type="button" class="btn btn-light btn-sm dropdown-toggle" data-bs-toggle="dropdown" + data-bs-auto-close="outside" > - - All Types + Types diff --git a/src/ims/element/incident/reports_template/template.xhtml b/src/ims/element/incident/reports_template/template.xhtml index c9aec8f3f..17e733155 100644 --- a/src/ims/element/incident/reports_template/template.xhtml +++ b/src/ims/element/incident/reports_template/template.xhtml @@ -60,20 +60,20 @@ All Days @@ -86,17 +86,17 @@ All Rows diff --git a/src/ims/element/static/field_reports.js b/src/ims/element/static/field_reports.js index b34accb7b..200c8d9c2 100644 --- a/src/ims/element/static/field_reports.js +++ b/src/ims/element/static/field_reports.js @@ -228,12 +228,13 @@ function initTableButtons() { .children(".col-sm-6:first") .replaceWith($("#button_container")); - const urlParams = new URLSearchParams(window.location.search); + const fragment = window.location.hash.startsWith("#") ? window.location.hash.substring(1) : window.location.hash; + const fragmentParams = new URLSearchParams(fragment); // Set button defaults - showDays(urlParams.get("days")??defaultDaysBack, false); - showRows(urlParams.get("rows")??defaultRows, false); + showDays(fragmentParams.get("days")??defaultDaysBack, false); + showRows(fragmentParams.get("rows")??defaultRows, false); } @@ -256,7 +257,7 @@ function initSearchField() { const searchInput = document.getElementById("search_input"); const searchAndDraw = function () { - pushWindowState(); + replaceWindowState(); let q = searchInput.value; let isRegex = false; if (q.startsWith("/") && q.endsWith("/")) { @@ -267,8 +268,9 @@ function initSearchField() { fieldReportsTable.draw(); } - const urlParams = new URLSearchParams(window.location.search); - const queryString = urlParams.get("q"); + const fragment = window.location.hash.startsWith("#") ? window.location.hash.substring(1) : window.location.hash; + const fragmentParams = new URLSearchParams(fragment); + const queryString = fragmentParams.get("q"); if (queryString) { searchInput.value = queryString; searchAndDraw(); @@ -438,6 +440,6 @@ function replaceWindowState() { // Next step is to create search params for the other filters too - const newURL = `${urlReplace(url_viewFieldReports)}?${new URLSearchParams(newParams).toString()}`; + const newURL = `${urlReplace(url_viewFieldReports)}#${new URLSearchParams(newParams).toString()}`; window.history.replaceState(null, null, newURL); } diff --git a/src/ims/element/static/incidents.js b/src/ims/element/static/incidents.js index 147e9bff6..8f2c520c4 100644 --- a/src/ims/element/static/incidents.js +++ b/src/ims/element/static/incidents.js @@ -312,29 +312,41 @@ function initTableButtons() { .children(".col-sm-6:first") .replaceWith($("#button_container")); + $(document).on('click', '.dropdown-item-checkable', function(event) { + event.preventDefault(); + $(this).toggleClass('dropdown-item-checked'); + showCheckedTypes(true); + }); + const $typeFilter = $("#ul_show_type"); for (const i in allIncidentTypes) { const type = allIncidentTypes[i]; - const $a = $("", {class: "name dropdown-item", href:"#"}); + const $a = $("", { + class: "dropdown-item dropdown-item-checkable dropdown-item-checked", + href:"#", + }); $a.text(type.toString()); - const $li = $("
  • ", {id: "show_type_" + i, onclick: "showType(" + i + ", true)"}); - $li.append($a); - $typeFilter.append($li); + $typeFilter.append($a); } - const urlParams = new URLSearchParams(window.location.search); + const fragment = window.location.hash.startsWith("#") ? window.location.hash.substring(1) : window.location.hash; + const fragmentParams = new URLSearchParams(fragment); // Set button defaults - const type = urlParams.get("type"); - if (type && allIncidentTypes.indexOf(type) !== -1) { - showType(allIncidentTypes.indexOf(type), false); - } else { - showType(defaultType, false); + const types = fragmentParams.getAll("type"); + const validTypes = []; + for (const t of types) { + console.log(`reading type ${t} checking`); + if (t && allIncidentTypes.indexOf(t) !== -1) { + validTypes.push(t); + } } - showState(urlParams.get("state")??defaultState, false); - showDays(urlParams.get("days")??defaultDaysBack, false); - showRows(urlParams.get("rows")??defaultRows, false); + setCheckedTypes(validTypes); + showCheckedTypes(false); + showState(fragmentParams.get("state")??defaultState, false); + showDays(fragmentParams.get("days")??defaultDaysBack, false); + showRows(fragmentParams.get("rows")??defaultRows, false); } @@ -368,8 +380,9 @@ function initSearchField() { incidentsTable.draw(); } - const urlParams = new URLSearchParams(window.location.search); - const queryString = urlParams.get("q"); + const fragment = window.location.hash.startsWith("#") ? window.location.hash.substring(1) : window.location.hash; + const fragmentParams = new URLSearchParams(fragment); + const queryString = fragmentParams.get("q"); if (queryString) { searchInput.value = queryString; searchAndDraw(); @@ -456,19 +469,11 @@ function initSearch() { return false } - switch (_showType) { - case null: - // fallthrough - case "all": - break; - default: - if (_showType >= 0 && _showType < allIncidentTypes.length) { - const st = allIncidentTypes[_showType]; - if (!(incident.incident_types??[]).includes(st)) { - return false; - } - } - break; + if (_showTypes && _showTypes.length > 0) { + const intersect = Object.values(incident.incident_types).filter(t => _showTypes.includes(t)).length > 0; + if (!intersect) { + return false; + } } return true; @@ -547,26 +552,34 @@ function showDays(daysBackToShow, replaceState) { // Show type button handling // -// _showType will be one of: -// "all" or null (meaning show everything) -// a numeric index into allIncidentTypes -let _showType = null; -const defaultType = "all"; - -function showType(typeToShow, replaceState) { - // see _showType above for values of "typeToShow" - const id = typeToShow??defaultType; +// list of Incident Types to show, in text form +let _showTypes = []; - const $menu = $("#show_type"); - const $item = $("#show_type_" + id); +function setCheckedTypes(types) { + for (const $type of $('#ul_show_type > a')) { + if (types.length === 0 || types.includes($type.innerHTML)) { + $type.classList.add("dropdown-item-checked") + } else { + $type.classList.remove("dropdown-item-checked") + } + } +} - // Get title from selected item - const selection = $item.children(".name").html(); +function readCheckedTypes() { + const checkedTypes = []; + for (const $type of $('#ul_show_type > a')) { + if ($type.classList.contains("dropdown-item-checked")) { + checkedTypes.push($type.innerHTML); + } + } + return checkedTypes; +} - // Update menu title to reflect selected item - $menu.children(".selection").html(selection); +function showCheckedTypes(replaceState) { + _showTypes = readCheckedTypes(); - _showType = typeToShow; + const numTypes = _showTypes.length === allIncidentTypes.length ? "All" : _showTypes.length; + document.getElementById("show_type").textContent = `${numTypes} Types`; if (replaceState) { replaceWindowState(); @@ -618,8 +631,10 @@ function replaceWindowState() { if (searchVal) { newParams.push(["q", searchVal]); } - if (_showType != null && _showType !== defaultType) { - newParams.push(["type", allIncidentTypes[_showType]]); + if (_showTypes != null && _showTypes.length !== allIncidentTypes.length) { + for (const t of _showTypes) { + newParams.push(["type", t]); + } } if (_showState != null && _showState !== defaultState) { newParams.push(["state", _showState]); @@ -631,8 +646,6 @@ function replaceWindowState() { newParams.push(["rows", _showRows]); } - // Next step is to create search params for the other filters too - - const newURL = `${viewIncidentsURL}?${new URLSearchParams(newParams).toString()}`; + const newURL = `${viewIncidentsURL}#${new URLSearchParams(newParams).toString()}`; window.history.replaceState(null, null, newURL); } diff --git a/src/ims/element/static/style.css b/src/ims/element/static/style.css index 59fba7bb1..e0c135ce6 100644 --- a/src/ims/element/static/style.css +++ b/src/ims/element/static/style.css @@ -316,3 +316,14 @@ h1 { .hidden { display: none !important; } + +.dropdown-item-checkable { + padding: var(--bs-dropdown-item-padding-y) 1.5rem; +} + +.dropdown-item-checked::before { + position: absolute; + left: .4rem; + content: '✓'; + font-weight: 600; +}