fix: resolve all CI lint errors and security audit failures (v0.10.9 Beta)

- DevicesPage: add statusFilter + typeFilter to filtered useMemo deps (was a real bug — filter chips had no effect until unrelated re-render)
- DevicesPage: refactor cpuHistories useMemo to pure Object.fromEntries form
- DashboardPage: suppress react-hooks/purity for intentional Date.now() chart domain
- Sidebar: move GroupHeader from inline arrow fn to module-scope named function (fixes react-hooks/static-components)
- axios: bump ^1.6.5 → ^1.16.0 to clear high-severity CVEs (prototype pollution, CRLF injection, NO_PROXY bypass)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2GT-Rich
2026-05-11 16:58:23 -07:00
co-authored by Claude Sonnet 4.6
parent 3a7ac1d023
commit 17e989da99
5 changed files with 53 additions and 52 deletions
+28 -27
View File
@@ -86,6 +86,34 @@ function NavItem({
);
}
function GroupHeader({
icon: Icon,
label,
isActive,
isOpen,
onToggle,
}: {
icon: React.ElementType;
label: string;
isActive: boolean;
isOpen: boolean;
onToggle: () => void;
}) {
return (
<button
onClick={onToggle}
className={clsx(
'w-full flex items-center gap-[10px] px-[10px] py-[7px] rounded-[6px] text-[13px] font-medium transition-colors duration-150 border-l-2 border-transparent',
isActive ? 'text-accent' : 'text-ink-2 hover:bg-surface-3 hover:text-ink'
)}
>
<Icon className="w-[15px] h-[15px] flex-shrink-0" />
<span className="flex-1 text-left">{label}</span>
<ChevronDown className={clsx('w-3.5 h-3.5 transition-transform', isOpen ? 'rotate-0' : '-rotate-90')} />
</button>
);
}
export default function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
const [collapsed, setCollapsed] = useState(false);
const location = useLocation();
@@ -124,33 +152,6 @@ export default function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
onMobileClose();
};
// Group header button (Switches / Routers / Wireless / Network Services)
const GroupHeader = ({
icon: Icon,
label,
isActive,
isOpen,
onToggle,
}: {
icon: React.ElementType;
label: string;
isActive: boolean;
isOpen: boolean;
onToggle: () => void;
}) => (
<button
onClick={onToggle}
className={clsx(
'w-full flex items-center gap-[10px] px-[10px] py-[7px] rounded-[6px] text-[13px] font-medium transition-colors duration-150 border-l-2 border-transparent',
isActive ? 'text-accent' : 'text-ink-2 hover:bg-surface-3 hover:text-ink'
)}
>
<Icon className="w-[15px] h-[15px] flex-shrink-0" />
<span className="flex-1 text-left">{label}</span>
<ChevronDown className={clsx('w-3.5 h-3.5 transition-transform', isOpen ? 'rotate-0' : '-rotate-90')} />
</button>
);
return (
<aside
className={clsx(
+7 -8
View File
@@ -274,14 +274,13 @@ function SummaryView(props: Record<string, any>) {
router: { label: 'Routers', color: 'var(--violet)' },
};
const chartDomain = useMemo<[number, number]>(
() => {
const ranges: Record<string, number> = { '1h': 1, '6h': 6, '24h': 24, '7d': 168 };
const hrs = ranges[chartRange] ?? 24;
return [Date.now() - hrs * 60 * 60 * 1000, Date.now()];
},
[chartRange, clientsOverTime] // eslint-disable-line react-hooks/exhaustive-deps
);
const chartDomain: [number, number] = (() => {
const ranges: Record<string, number> = { '1h': 1, '6h': 6, '24h': 24, '7d': 168 };
const hrs = ranges[chartRange] ?? 24;
// eslint-disable-next-line react-hooks/purity
const now = Date.now();
return [now - hrs * 60 * 60 * 1000, now];
})();
return (
<div className="space-y-4">
+10 -9
View File
@@ -161,14 +161,15 @@ export default function DevicesPage() {
enabled: d.status === 'online',
})),
});
const cpuHistories = useMemo(() => {
const map: Record<number, number[]> = {};
devices.forEach((d, i) => {
const pts = cpuHistoryResults[i]?.data ?? [];
map[d.id] = pts.map((p: { cpu_load?: number }) => p.cpu_load ?? 0).filter((v: number) => v > 0);
});
return map;
}, [devices, cpuHistoryResults]);
const cpuHistories = useMemo(
() => Object.fromEntries(
devices.map((d, i) => {
const pts = cpuHistoryResults[i]?.data ?? [];
return [d.id, pts.map((p: { cpu_load?: number }) => p.cpu_load ?? 0).filter((v: number) => v > 0)];
})
) as Record<number, number[]>,
[devices, cpuHistoryResults],
);
const filtered = useMemo(() => {
const base = devices.filter(d => {
@@ -205,7 +206,7 @@ export default function DevicesPage() {
return deviceSort.dir === 'asc' ? cmp : -cmp;
});
return sorted;
}, [devices, search, deviceSort]);
}, [devices, search, statusFilter, typeFilter, deviceSort]);
const discoveredList = discovered as DiscoveredDevice[];
const duplicateCount = useMemo(