Add search result type filters and fix dashboard caching

- Add granular filter buttons for search results: Equipment, PCs, Servers,
  Switches, Access Points (instead of single "Machines" filter)
- Fix network devices showing as "Machine" - now shows actual type (Server,
  Switch, Access Point, etc.) with appropriate icons
- Add cache-busting timestamp to shopfloor dashboard API fetch to prevent
  stale data from browser/proxy caching
- Fix search results page width (col-lg-auto to col-12)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-01-23 11:25:47 -05:00
parent 1b7946900c
commit 365d18d334
2 changed files with 644 additions and 151 deletions

View File

@@ -262,7 +262,7 @@ Set rs = Nothing
<div class="clearfix"></div>
<div class="content-wrapper">
<div class="row">
<div class="col-lg-auto">
<div class="col-12">
<div class="card">
<div class="card-body">
<ul class="nav nav-tabs nav-tabs-primary top-icon nav-justified">
@@ -319,6 +319,44 @@ Set rs = Nothing
<h5 class="card-title">Search Results</h5>
<!-- Result Type Filter -->
<div id="resultFilters" class="result-filters" style="display:none; margin-bottom: 15px;">
<span style="font-weight: 600; margin-right: 10px; color: #888;">Filter by:</span>
<button class="filter-btn active" data-filter="all" onclick="filterResults('all')">
<i class="zmdi zmdi-view-list"></i> All <span class="filter-count" id="count-all"></span>
</button>
<button class="filter-btn" data-filter="app" onclick="filterResults('app')" style="display:none;">
<i class="zmdi zmdi-apps"></i> Applications <span class="filter-count" id="count-app"></span>
</button>
<button class="filter-btn" data-filter="kb" onclick="filterResults('kb')" style="display:none;">
<i class="zmdi zmdi-book"></i> KB Articles <span class="filter-count" id="count-kb"></span>
</button>
<button class="filter-btn" data-filter="equipment" onclick="filterResults('equipment')" style="display:none;">
<i class="zmdi zmdi-reader"></i> Equipment <span class="filter-count" id="count-equipment"></span>
</button>
<button class="filter-btn" data-filter="pc" onclick="filterResults('pc')" style="display:none;">
<i class="zmdi zmdi-desktop-windows"></i> PCs <span class="filter-count" id="count-pc"></span>
</button>
<button class="filter-btn" data-filter="server" onclick="filterResults('server')" style="display:none;">
<i class="zmdi zmdi-dns"></i> Servers <span class="filter-count" id="count-server"></span>
</button>
<button class="filter-btn" data-filter="switch" onclick="filterResults('switch')" style="display:none;">
<i class="zmdi zmdi-device-hub"></i> Switches <span class="filter-count" id="count-switch"></span>
</button>
<button class="filter-btn" data-filter="accesspoint" onclick="filterResults('accesspoint')" style="display:none;">
<i class="zmdi zmdi-wifi"></i> Access Points <span class="filter-count" id="count-accesspoint"></span>
</button>
<button class="filter-btn" data-filter="printer" onclick="filterResults('printer')" style="display:none;">
<i class="zmdi zmdi-print"></i> Printers <span class="filter-count" id="count-printer"></span>
</button>
<button class="filter-btn" data-filter="employee" onclick="filterResults('employee')" style="display:none;">
<i class="zmdi zmdi-account"></i> Employees <span class="filter-count" id="count-employee"></span>
</button>
<button class="filter-btn" data-filter="notification" onclick="filterResults('notification')" style="display:none;">
<i class="zmdi zmdi-notifications-none"></i> Notifications <span class="filter-count" id="count-notification"></span>
</button>
</div>
<%
' Show educational banner if this is a shared link
If isShared And search <> "" Then
@@ -488,11 +526,13 @@ Set rsNotif = Nothing
' Now get Machines (by machine number, alias, notes, machine type, or vendor)
' Also search by hostname for PCs
' Note: Join machinetypes directly to machines.machinetypeid (not through models)
' This ensures network devices show correct type (Firewall, Switch, etc.) instead of "Machine"
Dim rsMachines
strSQL = "SELECT m.machineid, m.machinenumber, m.alias, m.hostname, m.pctypeid, mt.machinetype " & _
"FROM machines m " & _
"LEFT JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid " & _
"LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid " & _
"LEFT JOIN machinetypes mt ON mo.machinetypeid = mt.machinetypeid " & _
"LEFT JOIN vendors v ON mo.vendorid = v.vendorid " & _
"WHERE (m.machinenumber LIKE ? OR m.alias LIKE ? OR m.machinenotes LIKE ? OR mt.machinetype LIKE ? OR v.vendor LIKE ? OR m.hostname LIKE ?) " & _
" AND m.isactive = 1 " & _
@@ -707,7 +747,7 @@ Next
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
Response.Write("<tr id='" & rowId & "' data-type='app'" & rowStyle & ">")
' Column 1: Empty for apps
Response.Write("<td>&nbsp;</td>")
@@ -740,7 +780,7 @@ Next
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
Response.Write("<tr id='" & rowId & "' data-type='kb'" & rowStyle & ">")
' Column 1: Edit icon
Response.Write("<td><a href='./editlink.asp?linkid=" & linkid & "'><i class='zmdi zmdi-edit' title='Edit This Link'></i></a></td>")
@@ -773,7 +813,7 @@ Next
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
Response.Write("<tr id='" & rowId & "' data-type='notification'" & rowStyle & ">")
' Column 1: Empty for notifications
Response.Write("<td>&nbsp;</td>")
@@ -802,19 +842,56 @@ Next
Dim machineIsPC, machineDetailPage, machineListPage, machineIcon, machineIconColor, machineLabel
machineIsPC = (UBound(dataFields) >= 3 And dataFields(3) = "True")
' Set page, icon and label based on PC vs Equipment
' Set page, icon, label and filter data-type based on PC vs Equipment vs Network Device
Dim machineDataType
If machineIsPC Then
machineDetailPage = "displaypc.asp"
machineListPage = "displaypcs.asp"
machineIcon = "zmdi-desktop-windows"
machineIconColor = "text-primary"
machineLabel = "PC"
machineDataType = "pc"
Else
machineDetailPage = "displaymachine.asp"
machineListPage = "displaymachines.asp"
machineIcon = "zmdi-reader"
machineIconColor = "text-warning"
machineLabel = "Machine"
' Use actual machine type from database for label (Server, Switch, etc.)
' Fall back to "Machine" if not available
If Not IsNull(machineType) And machineType <> "" Then
machineLabel = machineType
Else
machineLabel = "Machine"
End If
' Set icon and data-type based on machine type
Select Case LCase(machineLabel)
Case "server"
machineIcon = "zmdi-dns"
machineIconColor = "text-danger"
machineDataType = "server"
Case "switch"
machineIcon = "zmdi-device-hub"
machineIconColor = "text-info"
machineDataType = "switch"
Case "access point"
machineIcon = "zmdi-wifi"
machineIconColor = "text-success"
machineDataType = "accesspoint"
Case "firewall"
machineIcon = "zmdi-shield-security"
machineIconColor = "text-warning"
machineDataType = "equipment"
Case "idf"
machineIcon = "zmdi-input-composite"
machineIconColor = "text-secondary"
machineDataType = "equipment"
Case "camera"
machineIcon = "zmdi-videocam"
machineIconColor = "text-primary"
machineDataType = "equipment"
Case Else
machineIcon = "zmdi-reader"
machineIconColor = "text-warning"
machineDataType = "equipment"
End Select
End If
rowId = "machine-result-" & machineId
@@ -824,7 +901,7 @@ Next
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
Response.Write("<tr id='" & rowId & "' data-type='" & machineDataType & "'" & rowStyle & ">")
' Column 1: Empty for machines
Response.Write("<td>&nbsp;</td>")
@@ -855,7 +932,7 @@ Next
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
Response.Write("<tr id='" & rowId & "' data-type='printer'" & rowStyle & ">")
' Column 1: Empty for printers
Response.Write("<td>&nbsp;</td>")
@@ -892,7 +969,7 @@ Next
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
Response.Write("<tr id='" & rowId & "' data-type='employee'" & rowStyle & ">")
' Column 1: Empty for employees
Response.Write("<td>&nbsp;</td>")
@@ -960,6 +1037,44 @@ Next
<script src="assets/js/app-script.js"></script>
<style>
/* Result type filter buttons */
.result-filters {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.filter-btn {
padding: 6px 14px;
font-size: 13px;
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 20px;
background: rgba(102, 126, 234, 0.1);
color: #667eea;
cursor: pointer;
transition: all 0.2s ease;
}
.filter-btn:hover {
background: rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.5);
}
.filter-btn.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-color: transparent;
}
.filter-btn i {
margin-right: 5px;
}
.filter-count {
font-size: 11px;
opacity: 0.8;
margin-left: 4px;
}
.filter-btn.active .filter-count {
opacity: 1;
}
/* Highlighter effect - works in both dark and light themes */
.highlighted-result {
background-color: rgba(255, 235, 59, 0.25) !important; /* Semi-transparent yellow */
@@ -1182,8 +1297,88 @@ function showToast() {
}, 3500);
}
// Filter results by type
function filterResults(type) {
var rows = document.querySelectorAll('#searchResults tbody tr[data-type]');
var filterBar = document.getElementById('resultFilters');
var buttons = filterBar.querySelectorAll('.filter-btn');
// Update active button
buttons.forEach(function(btn) {
btn.classList.remove('active');
if (btn.getAttribute('data-filter') === type) {
btn.classList.add('active');
}
});
// Show/hide rows based on type
rows.forEach(function(row) {
if (type === 'all' || row.getAttribute('data-type') === type) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
}
// Initialize filter counts and show filter bar if multiple types
function initializeFilters() {
var rows = document.querySelectorAll('#searchResults tbody tr[data-type]');
var counts = {
all: 0,
app: 0,
kb: 0,
equipment: 0,
pc: 0,
server: 0,
switch: 0,
accesspoint: 0,
printer: 0,
employee: 0,
notification: 0
};
var typesFound = {};
// Count rows by type
rows.forEach(function(row) {
var type = row.getAttribute('data-type');
counts.all++;
if (counts[type] !== undefined) {
counts[type]++;
typesFound[type] = true;
}
});
// Update count badges
for (var type in counts) {
var countEl = document.getElementById('count-' + type);
if (countEl) {
countEl.textContent = '(' + counts[type] + ')';
}
}
// Show filter buttons only for types that exist
var filterBar = document.getElementById('resultFilters');
var buttons = filterBar.querySelectorAll('.filter-btn[data-filter]');
buttons.forEach(function(btn) {
var filter = btn.getAttribute('data-filter');
if (filter === 'all' || typesFound[filter]) {
btn.style.display = '';
}
});
// Show filter bar only if there are multiple types
var numTypes = Object.keys(typesFound).length;
if (numTypes > 1) {
filterBar.style.display = 'flex';
}
}
// Scroll to highlighted result on page load
window.addEventListener('DOMContentLoaded', function() {
// Initialize filter bar
initializeFilters();
var urlParams = new URLSearchParams(window.location.search);
var highlightId = urlParams.get('highlight');
var highlightType = urlParams.get('type');