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:
co-authored by
Claude Sonnet 4.6
parent
3a7ac1d023
commit
17e989da99
@@ -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(
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user