diff --git a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt index 6f210ddd..55a98c62 100644 --- a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt +++ b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt @@ -1 +1 @@ -721 +722 diff --git a/src/Web/autopilot-monitor-web/app/dashboard/components/SessionTable.tsx b/src/Web/autopilot-monitor-web/app/dashboard/components/SessionTable.tsx index c63ff99c..e61e191c 100644 --- a/src/Web/autopilot-monitor-web/app/dashboard/components/SessionTable.tsx +++ b/src/Web/autopilot-monitor-web/app/dashboard/components/SessionTable.tsx @@ -76,6 +76,7 @@ interface SessionTableProps { onTenantIdFilterChange: (value: string) => void; onTenantIdFilterSubmit: () => void; onTenantIdFilterClear: () => void; + tenantList: { tenantId: string; domainName: string }[]; blockedDevicesSet: Set; isPreviewBlocked: boolean; user: { isGlobalAdmin?: boolean } | null; @@ -112,6 +113,7 @@ export function SessionTable({ onTenantIdFilterChange, onTenantIdFilterSubmit, onTenantIdFilterClear, + tenantList, blockedDevicesSet, isPreviewBlocked, user, @@ -126,6 +128,18 @@ export function SessionTable({ const [openFilterColumn, setOpenFilterColumn] = useState(null); const columnSelectorRef = useRef(null); const filterDropdownRef = useRef(null); + const tenantDropdownRef = useRef(null); + const [showTenantSuggestions, setShowTenantSuggestions] = useState(false); + const [tenantSelectedIndex, setTenantSelectedIndex] = useState(-1); + + // Fuzzy-match tenants by domain name or tenant ID + const tenantSuggestions = (() => { + const q = tenantIdFilter.trim().toLowerCase(); + if (q.length < 2 || tenantList.length === 0) return []; + return tenantList + .filter((t) => t.domainName.toLowerCase().includes(q) || t.tenantId.toLowerCase().includes(q)) + .slice(0, 8); + })(); // Persist visible columns to localStorage useEffect(() => { @@ -141,12 +155,15 @@ export function SessionTable({ if (filterDropdownRef.current && !filterDropdownRef.current.contains(e.target as Node)) { setOpenFilterColumn(null); } + if (tenantDropdownRef.current && !tenantDropdownRef.current.contains(e.target as Node)) { + setShowTenantSuggestions(false); + } } - if (showColumnSelector || openFilterColumn) { + if (showColumnSelector || openFilterColumn || showTenantSuggestions) { document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); } - }, [showColumnSelector, openFilterColumn]); + }, [showColumnSelector, openFilterColumn, showTenantSuggestions]); // Compute unique values for a filterable field from all sessions (not filtered) const getUniqueValues = (field: keyof Session): string[] => { @@ -283,14 +300,46 @@ export function SessionTable({ {/* Tenant ID Filter (Global Admin only) */} {globalAdminMode && (
-
+
onTenantIdFilterChange(e.target.value)} + onChange={(e) => { + onTenantIdFilterChange(e.target.value); + setShowTenantSuggestions(true); + setTenantSelectedIndex(-1); + }} + onFocus={() => { + if (tenantSuggestions.length > 0) setShowTenantSuggestions(true); + }} onKeyDown={(e) => { + if (showTenantSuggestions && tenantSuggestions.length > 0) { + if (e.key === "ArrowDown") { + e.preventDefault(); + setTenantSelectedIndex((i) => Math.min(i + 1, tenantSuggestions.length - 1)); + return; + } + if (e.key === "ArrowUp") { + e.preventDefault(); + setTenantSelectedIndex((i) => Math.max(i - 1, -1)); + return; + } + if (e.key === "Enter" && tenantSelectedIndex >= 0) { + e.preventDefault(); + const selected = tenantSuggestions[tenantSelectedIndex]; + onTenantIdFilterChange(selected.tenantId); + setShowTenantSuggestions(false); + setTenantSelectedIndex(-1); + return; + } + if (e.key === "Escape") { + setShowTenantSuggestions(false); + return; + } + } if (e.key === "Enter") { + setShowTenantSuggestions(false); onTenantIdFilterSubmit(); } }} @@ -298,7 +347,10 @@ export function SessionTable({ /> {tenantIdFilter && ( )} + {/* Tenant suggestions dropdown */} + {showTenantSuggestions && tenantSuggestions.length > 0 && ( +
+ {tenantSuggestions.map((t, idx) => ( + + ))} +
+ )}