diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68875a83..e77495ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,6 +77,13 @@ jobs: - name: Typecheck run: npx tsc --noEmit + # Errors fail outright. Warnings are ratcheted: the cap is the current + # count of accepted warnings (React-Compiler hooks-rule findings demoted + # in eslint.config.mjs + exhaustive-deps) — when you fix warnings, lower + # the cap to match; never raise it. + - name: Lint + run: npm run lint -- --max-warnings 163 + - name: Tests run: npm test diff --git a/src/Web/autopilot-monitor-web/app/admin/AdminPageSections.tsx b/src/Web/autopilot-monitor-web/app/admin/AdminPageSections.tsx index 8cb0c028..d6076bce 100644 --- a/src/Web/autopilot-monitor-web/app/admin/AdminPageSections.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/AdminPageSections.tsx @@ -3,24 +3,15 @@ import { useMemo } from "react"; import { usePageSections } from "../../hooks/usePageSections"; import { PageSectionItem } from "../../contexts/SidebarContext"; +import { route } from "../../lib/routes"; import { GearIcon, - NoSymbolIcon, DocumentTextIcon, ChartBarIcon, BuildingOfficeIcon, ShieldCheckIcon, } from "../../lib/sidebarIcons"; -// Inline icon (same as MetricsSidebar had) -function TrendingUpIcon({ className = "w-5 h-5" }: { className?: string }) { - return ( - - - - ); -} - // Inline icon for Ops (wrench) function WrenchIcon({ className = "w-5 h-5" }: { className?: string }) { return ( @@ -47,29 +38,29 @@ function GlobeIcon({ className = "w-5 h-5" }: { className?: string }) { export function AdminPageSections() { const items: PageSectionItem[] = useMemo(() => [ // Tenants - { id: "management", label: "Tenant Management", href: "/admin/tenants/management", group: "Tenants", groupIcon: }, - { id: "config-report", label: "Config Report", href: "/admin/tenants/config-report", group: "Tenants" }, + { id: "management", label: "Tenant Management", href: route("/admin/tenants/management"), group: "Tenants", groupIcon: }, + { id: "config-report", label: "Config Report", href: route("/admin/tenants/config-report"), group: "Tenants" }, // Metrics - { id: "platform-metrics", label: "Platform Metrics", href: "/admin/metrics/platform-metrics", group: "Metrics", groupIcon: }, - { id: "usage", label: "Platform Usage", href: "/admin/metrics/usage", group: "Metrics" }, - { id: "mcp-usage", label: "MCP Usage", href: "/admin/metrics/mcp-usage", group: "Metrics" }, + { id: "platform-metrics", label: "Platform Metrics", href: route("/admin/metrics/platform-metrics"), group: "Metrics", groupIcon: }, + { id: "usage", label: "Platform Usage", href: route("/admin/metrics/usage"), group: "Metrics" }, + { id: "mcp-usage", label: "MCP Usage", href: route("/admin/metrics/mcp-usage"), group: "Metrics" }, // Reports - { id: "session-reports", label: "Session Reports", href: "/admin/reports/session-reports", group: "Reports", groupIcon: }, - { id: "user-feedback", label: "User Feedback", href: "/admin/reports/user-feedback", group: "Reports" }, - { id: "session-export", label: "Session Export", href: "/admin/reports/session-export", group: "Reports" }, + { id: "session-reports", label: "Session Reports", href: route("/admin/reports/session-reports"), group: "Reports", groupIcon: }, + { id: "user-feedback", label: "User Feedback", href: route("/admin/reports/user-feedback"), group: "Reports" }, + { id: "session-export", label: "Session Export", href: route("/admin/reports/session-export"), group: "Reports" }, // Security - { id: "device-block", label: "Device Block", href: "/admin/security/device-block", group: "Security", groupIcon: }, - { id: "version-block", label: "Version Block", href: "/admin/security/version-block", group: "Security" }, - { id: "vulnerability-data", label: "Vulnerability Data", href: "/admin/security/vulnerability-data", group: "Security" }, + { id: "device-block", label: "Device Block", href: route("/admin/security/device-block"), group: "Security", groupIcon: }, + { id: "version-block", label: "Version Block", href: route("/admin/security/version-block"), group: "Security" }, + { id: "vulnerability-data", label: "Vulnerability Data", href: route("/admin/security/vulnerability-data"), group: "Security" }, // Settings - { id: "global", label: "Global Settings", href: "/admin/settings/global", group: "Settings", groupIcon: }, - { id: "diagnostics-log-paths", label: "Diagnostics Log Paths", href: "/admin/settings/diagnostics-log-paths", group: "Settings" }, - { id: "config-reseed", label: "Config Reseed", href: "/admin/settings/config-reseed", group: "Settings" }, - { id: "usage-plans", label: "Usage Plans", href: "/admin/settings/usage-plans", group: "Settings" }, + { id: "global", label: "Global Settings", href: route("/admin/settings/global"), group: "Settings", groupIcon: }, + { id: "diagnostics-log-paths", label: "Diagnostics Log Paths", href: route("/admin/settings/diagnostics-log-paths"), group: "Settings" }, + { id: "config-reseed", label: "Config Reseed", href: route("/admin/settings/config-reseed"), group: "Settings" }, + { id: "usage-plans", label: "Usage Plans", href: route("/admin/settings/usage-plans"), group: "Settings" }, // Ops (single page) { id: "ops", label: "Maintenance", href: "/admin/ops", group: "Ops", groupIcon: }, diff --git a/src/Web/autopilot-monitor-web/app/admin/backups/components/RestoreRowDiffModal.tsx b/src/Web/autopilot-monitor-web/app/admin/backups/components/RestoreRowDiffModal.tsx index 3207c10a..11722896 100644 --- a/src/Web/autopilot-monitor-web/app/admin/backups/components/RestoreRowDiffModal.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/backups/components/RestoreRowDiffModal.tsx @@ -90,7 +90,7 @@ export function RestoreRowDiffModal({ This is an authentication / authorization table (GlobalAdmins, TenantAdmins,{" "} McpUsers). Restoring this row will overwrite the live{" "} - IsEnabled flag — confirm that the backup row's enable/disable + IsEnabled flag — confirm that the backup row's enable/disable state is what you intend. diff --git a/src/Web/autopilot-monitor-web/app/admin/components/OpsAlertRulesSection.tsx b/src/Web/autopilot-monitor-web/app/admin/components/OpsAlertRulesSection.tsx index bcff312c..ff6b2ed1 100644 --- a/src/Web/autopilot-monitor-web/app/admin/components/OpsAlertRulesSection.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/components/OpsAlertRulesSection.tsx @@ -232,14 +232,6 @@ export function OpsAlertRulesSection({ const enabledRulesCount = rules.filter(r => r.enabled).length; const enabledProviders = [telegramEnabled, teamsEnabled, slackEnabled].filter(Boolean).length; - // Get category for a given event type - const getCategoryForEvent = (eventType: string): string => { - for (const [cat, types] of Object.entries(OPS_EVENT_TYPES)) { - if (types.includes(eventType)) return cat; - } - return "Unknown"; - }; - return (
{/* Alert Rules */} diff --git a/src/Web/autopilot-monitor-web/app/admin/components/OpsEventsSection.tsx b/src/Web/autopilot-monitor-web/app/admin/components/OpsEventsSection.tsx index c3eacc68..d74e1629 100644 --- a/src/Web/autopilot-monitor-web/app/admin/components/OpsEventsSection.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/components/OpsEventsSection.tsx @@ -1,6 +1,6 @@ "use client"; -import { sessionUrl } from "@/lib/routes"; +import { sessionUrl, deviceBlockUrl } from "@/lib/routes"; import { useCallback, useEffect, useState } from "react"; import Link from "next/link"; import { api } from "@/lib/api"; @@ -558,7 +558,6 @@ export function OpsEventsSection({ const sessionId = extractSessionId(selectedEvent.details); if (!sessionId) return null; const reason = buildAutoReason(selectedEvent.eventType, sessionId); - const baseHref = `/admin/security/device-block?sessionId=${encodeURIComponent(sessionId)}&reason=${encodeURIComponent(reason)}`; return (

@@ -574,14 +573,14 @@ export function OpsEventsSection({ View session setSelectedEvent(null)} className="inline-flex items-center px-3 py-1.5 rounded-md text-xs font-medium bg-orange-100 text-orange-800 hover:bg-orange-200 dark:bg-orange-900/40 dark:text-orange-200 dark:hover:bg-orange-900/60 border border-orange-300 dark:border-orange-700" > Block this device setSelectedEvent(null)} className="inline-flex items-center px-3 py-1.5 rounded-md text-xs font-medium bg-red-700 text-white hover:bg-red-800 dark:bg-red-700 dark:hover:bg-red-800" > diff --git a/src/Web/autopilot-monitor-web/app/admin/components/TenantManagementSection.tsx b/src/Web/autopilot-monitor-web/app/admin/components/TenantManagementSection.tsx index bfd2c0fb..fa215dc7 100644 --- a/src/Web/autopilot-monitor-web/app/admin/components/TenantManagementSection.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/components/TenantManagementSection.tsx @@ -1,6 +1,6 @@ "use client"; -import { Suspense, useCallback, useEffect, useRef, useState } from "react"; +import { Suspense, useEffect, useRef, useState } from "react"; import { useSearchParams } from "next/navigation"; import { api } from "@/lib/api"; import { authenticatedFetch, TokenExpiredError } from "@/lib/authenticatedFetch"; diff --git a/src/Web/autopilot-monitor-web/app/admin/metrics/sections/SectionAgentMetrics.tsx b/src/Web/autopilot-monitor-web/app/admin/metrics/sections/SectionAgentMetrics.tsx index d7c4e47e..e1ae5b58 100644 --- a/src/Web/autopilot-monitor-web/app/admin/metrics/sections/SectionAgentMetrics.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/metrics/sections/SectionAgentMetrics.tsx @@ -1,7 +1,6 @@ 'use client'; import { useEffect, useState, useMemo, useCallback } from 'react'; -import { useRouter } from 'next/navigation'; import { api } from '@/lib/api'; import TruncatedLabel from '@/components/TruncatedLabel'; import { useAuth } from '../../../../contexts/AuthContext'; @@ -138,9 +137,7 @@ function pN(values: number[], percentile: number): number { // ── Component ────────────────────────────────────────────────────────────────── export function SectionAgentMetrics() { - const router = useRouter(); - - const { getAccessToken, user } = useAuth(); + const { getAccessToken } = useAuth(); const { addNotification } = useNotifications(); const [loading, setLoading] = useState(true); @@ -845,8 +842,6 @@ function StatCard({ label, value, detail, color }: { label: string; value: strin function FootprintBadge({ value, thresholds, - unit, - formatFn, }: { value: number; thresholds: [number, number, number]; diff --git a/src/Web/autopilot-monitor-web/app/admin/ops/session-cleanup/components/RestoreBrowserTab.tsx b/src/Web/autopilot-monitor-web/app/admin/ops/session-cleanup/components/RestoreBrowserTab.tsx index ef2a4a69..63068920 100644 --- a/src/Web/autopilot-monitor-web/app/admin/ops/session-cleanup/components/RestoreBrowserTab.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/ops/session-cleanup/components/RestoreBrowserTab.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { api } from "@/lib/api"; import { authenticatedFetch, TokenExpiredError } from "@/lib/authenticatedFetch"; import { useAdminConfig } from "../../../AdminConfigContext"; diff --git a/src/Web/autopilot-monitor-web/app/admin/ops/session-cleanup/page.tsx b/src/Web/autopilot-monitor-web/app/admin/ops/session-cleanup/page.tsx index 4ecc1dca..5a597606 100644 --- a/src/Web/autopilot-monitor-web/app/admin/ops/session-cleanup/page.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/ops/session-cleanup/page.tsx @@ -139,7 +139,6 @@ function TabButton({ function InFlightTab({ getAccessToken, setError, - setSuccessMessage, }: { getAccessToken: (forceRefresh?: boolean) => Promise; setError: (error: string | null) => void; diff --git a/src/Web/autopilot-monitor-web/app/admin/reports/[section]/SectionClient.tsx b/src/Web/autopilot-monitor-web/app/admin/reports/[section]/SectionClient.tsx index 3d77561a..ef5d5400 100644 --- a/src/Web/autopilot-monitor-web/app/admin/reports/[section]/SectionClient.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/reports/[section]/SectionClient.tsx @@ -1,7 +1,7 @@ "use client"; import { notFound } from "next/navigation"; -import { REPORTS_NAV_SECTIONS, type ReportsSectionId } from "../reportsNavSections"; +import { type ReportsSectionId } from "../reportsNavSections"; import { SectionSessionReports } from "../sections/SectionSessionReports"; import { SectionDistressReports } from "../sections/SectionDistressReports"; import { SectionUserFeedback } from "../sections/SectionUserFeedback"; diff --git a/src/Web/autopilot-monitor-web/app/admin/security/[section]/SectionClient.tsx b/src/Web/autopilot-monitor-web/app/admin/security/[section]/SectionClient.tsx index 32979c06..7e7b50f9 100644 --- a/src/Web/autopilot-monitor-web/app/admin/security/[section]/SectionClient.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/security/[section]/SectionClient.tsx @@ -1,7 +1,7 @@ "use client"; import { notFound } from "next/navigation"; -import { SECURITY_NAV_SECTIONS, type SecuritySectionId } from "../securityNavSections"; +import { type SecuritySectionId } from "../securityNavSections"; import { SectionDeviceBlock } from "../sections/SectionDeviceBlock"; import { SectionVersionBlock } from "../sections/SectionVersionBlock"; import { SectionVulnerabilityData } from "../sections/SectionVulnerabilityData"; diff --git a/src/Web/autopilot-monitor-web/app/admin/tenants/sections/SectionTenantConfigReport.tsx b/src/Web/autopilot-monitor-web/app/admin/tenants/sections/SectionTenantConfigReport.tsx index 7a90cc4c..d02c1f38 100644 --- a/src/Web/autopilot-monitor-web/app/admin/tenants/sections/SectionTenantConfigReport.tsx +++ b/src/Web/autopilot-monitor-web/app/admin/tenants/sections/SectionTenantConfigReport.tsx @@ -426,8 +426,6 @@ export function SectionTenantConfigReport() { })() : []; - const selectedTenant = tenants.find((t) => t.tenantId === selectedTenantId); - return (

{/* Header */} diff --git a/src/Web/autopilot-monitor-web/app/analyze-rules/components/TemplateConfigModal.tsx b/src/Web/autopilot-monitor-web/app/analyze-rules/components/TemplateConfigModal.tsx index e291fb4a..cef0db33 100644 --- a/src/Web/autopilot-monitor-web/app/analyze-rules/components/TemplateConfigModal.tsx +++ b/src/Web/autopilot-monitor-web/app/analyze-rules/components/TemplateConfigModal.tsx @@ -1,7 +1,7 @@ "use client"; import { useState, useEffect, useRef } from "react"; -import { AnalyzeRule, TemplateVariable } from "../types"; +import { AnalyzeRule } from "../types"; interface TemplateConfigModalProps { rule: AnalyzeRule; diff --git a/src/Web/autopilot-monitor-web/app/analyze-rules/page.tsx b/src/Web/autopilot-monitor-web/app/analyze-rules/page.tsx index 88d4a377..538cde34 100644 --- a/src/Web/autopilot-monitor-web/app/analyze-rules/page.tsx +++ b/src/Web/autopilot-monitor-web/app/analyze-rules/page.tsx @@ -1,8 +1,6 @@ "use client"; import { useEffect, useState, useCallback } from "react"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; import { ProtectedRoute } from "../../components/ProtectedRoute"; import { useAuth } from "../../contexts/AuthContext"; import { api } from "@/lib/api"; @@ -25,8 +23,6 @@ import TemplateConfigModal from "./components/TemplateConfigModal"; import { DOCS_URL } from "@/utils/config"; export default function AnalyzeRulesPage() { - const router = useRouter(); - const { user, getAccessToken } = useAuth(); const { successMessage, error, showSuccess, showError } = useNotificationMessages(); diff --git a/src/Web/autopilot-monitor-web/app/apps/detail/page.tsx b/src/Web/autopilot-monitor-web/app/apps/detail/page.tsx index 43c8d16e..a274b2c5 100644 --- a/src/Web/autopilot-monitor-web/app/apps/detail/page.tsx +++ b/src/Web/autopilot-monitor-web/app/apps/detail/page.tsx @@ -376,11 +376,6 @@ function AppDetailContent() { return `${d.getUTCMonth() + 1}/${d.getUTCDate()}`; } - const failureCodeBarColor = (row: Record): string => { - const c = Number(row.count); - return c >= 5 ? chartColors.danger : c >= 2 ? chartColors.warning : chartColors.muted; - }; - return (
diff --git a/src/Web/autopilot-monitor-web/app/audit/page.tsx b/src/Web/autopilot-monitor-web/app/audit/page.tsx index 6d2d751e..75bbc92d 100644 --- a/src/Web/autopilot-monitor-web/app/audit/page.tsx +++ b/src/Web/autopilot-monitor-web/app/audit/page.tsx @@ -2,7 +2,6 @@ import { Fragment, useCallback, useEffect, useState } from 'react'; import { TableSkeleton } from '@/components/skeletons/TableSkeleton'; -import { useRouter } from 'next/navigation'; import { useAuth } from '../../contexts/AuthContext'; import { useNotifications } from '../../contexts/NotificationContext'; import { ProtectedRoute } from '../../components/ProtectedRoute'; @@ -63,8 +62,6 @@ function dateInputToIsoEnd(value: string): string { } export default function AuditPage() { - const router = useRouter(); - const { getAccessToken } = useAuth(); const { addNotification } = useNotifications(); 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 c82aaaae..c684b8ca 100644 --- a/src/Web/autopilot-monitor-web/app/dashboard/components/SessionTable.tsx +++ b/src/Web/autopilot-monitor-web/app/dashboard/components/SessionTable.tsx @@ -2,6 +2,7 @@ import { sessionUrl } from "@/lib/routes"; import { useRouter } from "next/navigation"; +import type { Route } from "next"; import { useState, useEffect, useRef, useMemo, useDeferredValue } from "react"; import { Session } from "../types"; import { trackEvent } from "@/lib/appInsights"; @@ -103,7 +104,7 @@ interface SessionTableProps { /** Builds the row's navigation target. Defaults to `/sessions/{id}`; a cross-tenant viewer overrides it to * append `?tenantId=` so a delegated viewer opens the session in the managed tenant's (read-only) context. * Receives the whole session because the target tenant is per-row (session.tenantId), not derivable from id. */ - sessionLinkTarget?: (session: Session) => string; + sessionLinkTarget?: (session: Session) => Route; } export function SessionTable({ @@ -760,7 +761,6 @@ export function SessionTable({ function SessionCell({ columnKey, session, - adminMode, globalAdminMode, blockedDevicesSet, user, diff --git a/src/Web/autopilot-monitor-web/app/dashboard/components/WelcomeMessage.tsx b/src/Web/autopilot-monitor-web/app/dashboard/components/WelcomeMessage.tsx index e76cd60c..b3ed6983 100644 --- a/src/Web/autopilot-monitor-web/app/dashboard/components/WelcomeMessage.tsx +++ b/src/Web/autopilot-monitor-web/app/dashboard/components/WelcomeMessage.tsx @@ -1,6 +1,5 @@ "use client"; -import Link from "next/link"; import { DOCS_URL } from "@/utils/config"; export function WelcomeMessage() { diff --git a/src/Web/autopilot-monitor-web/app/dashboard/page.tsx b/src/Web/autopilot-monitor-web/app/dashboard/page.tsx index 8cb42912..6614657a 100644 --- a/src/Web/autopilot-monitor-web/app/dashboard/page.tsx +++ b/src/Web/autopilot-monitor-web/app/dashboard/page.tsx @@ -90,9 +90,8 @@ function HomeContent() { const mainClassName = fullWidth ? "w-full px-4 sm:px-6 lg:px-8 py-4" : "max-w-7xl mx-auto py-4 sm:px-6 lg:px-8"; - const { user, logout, getAccessToken, isPreviewBlocked, hasGlobalScope } = useAuth(); + const { user, getAccessToken, isPreviewBlocked, hasGlobalScope } = useAuth(); const { addNotification } = useNotifications(); - const [apiStatus, setApiStatus] = useState<"unchecked" | "checking" | "healthy" | "error">("unchecked"); // `?tenant=` deep-links a cross-tenant view onto one tenant — used by the /fleet card grid to drill // a managed tenant into this dashboard. Ignored for non-cross-tenant users (the filter is unused there). const initialTenantFilter = searchParams?.get("tenant") ?? ""; @@ -109,7 +108,7 @@ function HomeContent() { // Drives the stats refetch — server-side stats follow the submitted scope so // typing into the filter input doesn't trigger a backend round-trip per keystroke. const [submittedTenantIdFilter, setSubmittedTenantIdFilter] = useState(initialTenantFilter); - const { adminMode, setAdminMode, globalAdminMode, setGlobalAdminMode } = useAdminMode(); + const { adminMode, globalAdminMode, setGlobalAdminMode } = useAdminMode(); const signalR = useSignalR(); const { tenantId } = useTenant(); diff --git a/src/Web/autopilot-monitor-web/app/diagnosis/page.tsx b/src/Web/autopilot-monitor-web/app/diagnosis/page.tsx index a52031bd..f9833f29 100644 --- a/src/Web/autopilot-monitor-web/app/diagnosis/page.tsx +++ b/src/Web/autopilot-monitor-web/app/diagnosis/page.tsx @@ -802,12 +802,14 @@ function DiagnosisContent() { ); } -function EvidenceEventLinks({ matchedConditions, sessionId }: { matchedConditions: Record; sessionId: string }) { +function EvidenceEventLinks({ matchedConditions, sessionId }: { matchedConditions: Record; sessionId: string }) { const eventLinks: { signal: string; eventId: string; eventType?: string }[] = []; const seenEventIds = new Set(); - for (const [signal, evidence] of Object.entries(matchedConditions)) { + for (const [signal, evidenceRaw] of Object.entries(matchedConditions)) { if (signal.startsWith("factor_")) continue; + // Evidence values are rule-engine JSON; only object-shaped entries carry event links. + const evidence = evidenceRaw as { eventId?: string; eventType?: string } | string | null; if (evidence && typeof evidence === "object" && evidence.eventId) { // Multiple matched conditions can extract different fields from the // same event — one chip per distinct event, not per condition. diff --git a/src/Web/autopilot-monitor-web/app/fleet-health/page.tsx b/src/Web/autopilot-monitor-web/app/fleet-health/page.tsx index 1c524a54..e97f1f7f 100644 --- a/src/Web/autopilot-monitor-web/app/fleet-health/page.tsx +++ b/src/Web/autopilot-monitor-web/app/fleet-health/page.tsx @@ -2,6 +2,7 @@ import { useEffect, useState, useRef, useMemo } from "react"; import Link from "next/link"; +import type { Route } from "next"; import { ProtectedRoute } from "../../components/ProtectedRoute"; import { useSignalR } from "../../contexts/SignalRContext"; import { useTenant } from "../../contexts/TenantContext"; @@ -192,7 +193,7 @@ export default function FleetHealthPage() { // model. The model key is "{Manufacturer} {Model}", which the dashboard search matches // against its combined manufacturer+model text. Carry the selected tenant so a global // admin scoped to one tenant lands on that tenant's list rather than their default scope. - const dashboardModelHref = (model: string) => { + const dashboardModelHref = (model: string): Route => { const params = new URLSearchParams({ status: "Failed", search: model }); if (isGlobalAdmin && selectedTenantId) params.set("tenant", selectedTenantId); return `/dashboard?${params.toString()}`; diff --git a/src/Web/autopilot-monitor-web/app/gather-rules/components/GatherRuleCard.tsx b/src/Web/autopilot-monitor-web/app/gather-rules/components/GatherRuleCard.tsx index 4743b620..86f39e40 100644 --- a/src/Web/autopilot-monitor-web/app/gather-rules/components/GatherRuleCard.tsx +++ b/src/Web/autopilot-monitor-web/app/gather-rules/components/GatherRuleCard.tsx @@ -1,7 +1,7 @@ "use client"; import { useMemo, useState } from "react"; -import { GatherRule, NewRuleForm, CATEGORY_COLORS, COLLECTOR_TYPE_LABELS, EMPTY_FORM, formatTrigger, formatGatherPhase, withDerivedScopeMode } from "../types"; +import { GatherRule, NewRuleForm, CATEGORY_COLORS, COLLECTOR_TYPE_LABELS, formatTrigger, formatGatherPhase, withDerivedScopeMode } from "../types"; import { GatherRuleFormFields } from "./GatherRuleFormFields"; import { FormJsonToggle, JsonModeToggleButtons, ReadOnlyJsonView } from "@/components/rules/FormJsonToggle"; import { validateGatherRuleTarget } from "@/utils/guardValidation"; @@ -455,7 +455,7 @@ export function GatherRuleCard({ try { const parsed = JSON.parse(jsonText) as NewRuleForm; onSaveEdit(rule, withDerivedScopeMode({ ...editForm, ...parsed })); - } catch (e) { + } catch { // jsonError is handled by parent } } else { diff --git a/src/Web/autopilot-monitor-web/app/gather-rules/components/GatherRuleFormFields.tsx b/src/Web/autopilot-monitor-web/app/gather-rules/components/GatherRuleFormFields.tsx index 3df9bd03..a6510c13 100644 --- a/src/Web/autopilot-monitor-web/app/gather-rules/components/GatherRuleFormFields.tsx +++ b/src/Web/autopilot-monitor-web/app/gather-rules/components/GatherRuleFormFields.tsx @@ -363,7 +363,7 @@ export function GatherRuleFormFields({ form, setForm, showRuleId, unrestrictedMo />

XPath query to extract values. Examples: /root/element (path),{" "} - //element (anywhere),{" "} + {"//element"} (anywhere),{" "} /root/item[@attr='value'] (filter),{" "} /root/element/text() (text content).

diff --git a/src/Web/autopilot-monitor-web/app/gather-rules/page.tsx b/src/Web/autopilot-monitor-web/app/gather-rules/page.tsx index eb7a0301..4f1ee5b8 100644 --- a/src/Web/autopilot-monitor-web/app/gather-rules/page.tsx +++ b/src/Web/autopilot-monitor-web/app/gather-rules/page.tsx @@ -1,8 +1,6 @@ "use client"; import { useEffect, useState, useCallback } from "react"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; import { ProtectedRoute } from "../../components/ProtectedRoute"; import { useAuth } from "../../contexts/AuthContext"; import { api } from "@/lib/api"; @@ -16,14 +14,12 @@ import { FormJsonToggle, JsonModeToggleButtons } from "@/components/rules/FormJs import { useAuthenticatedFetch, useNotificationMessages, useGlobalAdminScope } from "@/hooks"; import { GlobalAdminBanner, globalAdminSubtitle } from "@/components/GlobalAdminBanner"; import { TenantScopeSelector } from "@/components/TenantScopeSelector"; -import { GatherRule, NewRuleForm, EMPTY_FORM, CATEGORY_COLORS, PHASE_TRIGGERS, buildScopeFields, validateScopeSelection, withDerivedScopeMode } from "./types"; +import { GatherRule, NewRuleForm, EMPTY_FORM, PHASE_TRIGGERS, buildScopeFields, validateScopeSelection, withDerivedScopeMode } from "./types"; import { GatherRuleFormFields } from "./components/GatherRuleFormFields"; import { GatherRuleCard } from "./components/GatherRuleCard"; import { DOCS_URL } from "@/utils/config"; export default function GatherRulesPage() { - const router = useRouter(); - const { user, getAccessToken } = useAuth(); const { successMessage, error, showSuccess, showError } = useNotificationMessages(); diff --git a/src/Web/autopilot-monitor-web/app/geographic-performance/page.tsx b/src/Web/autopilot-monitor-web/app/geographic-performance/page.tsx index 9dcdb576..79d180d5 100644 --- a/src/Web/autopilot-monitor-web/app/geographic-performance/page.tsx +++ b/src/Web/autopilot-monitor-web/app/geographic-performance/page.tsx @@ -144,7 +144,7 @@ export default function GeographicPerformancePage() { // Global admin tenant scope (aggregated-capable): tenant list, selection ("" = all tenants), // and scope flags. Default selection is the GA's own tenant. const scope = useAggregatedAdminScope(); - const { isGlobalAdmin, routeGlobal, selectedTenantId, isAggregatedGlobalView, scopeInitialized, scopeKey } = scope; + const { routeGlobal, selectedTenantId, isAggregatedGlobalView, scopeInitialized, scopeKey } = scope; const progress = useFetchProgress("geoPerf.lastFetchMs"); const { begin: progressBegin, finish: progressFinish } = progress; diff --git a/src/Web/autopilot-monitor-web/app/geographic-performance/sessions/page.tsx b/src/Web/autopilot-monitor-web/app/geographic-performance/sessions/page.tsx index f6f5ef9c..c7f8002d 100644 --- a/src/Web/autopilot-monitor-web/app/geographic-performance/sessions/page.tsx +++ b/src/Web/autopilot-monitor-web/app/geographic-performance/sessions/page.tsx @@ -223,7 +223,6 @@ function LocationSessionsContent() {