diff --git a/frontend/src/components/InlineGroupEdit.jsx b/frontend/src/components/InlineGroupEdit.jsx
index 6a7de28..3511c52 100644
--- a/frontend/src/components/InlineGroupEdit.jsx
+++ b/frontend/src/components/InlineGroupEdit.jsx
@@ -1,5 +1,11 @@
import { Check, ChevronDown, Edit2, X } from "lucide-react";
-import React, { useEffect, useMemo, useRef, useState } from "react";
+import React, {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
const InlineGroupEdit = ({
value,
@@ -38,7 +44,7 @@ const InlineGroupEdit = ({
}, [value, isEditing]);
// Calculate dropdown position
- const calculateDropdownPosition = () => {
+ const calculateDropdownPosition = useCallback(() => {
if (buttonRef.current) {
const rect = buttonRef.current.getBoundingClientRect();
setDropdownPosition({
@@ -47,7 +53,7 @@ const InlineGroupEdit = ({
width: rect.width,
});
}
- };
+ }, []);
// Close dropdown when clicking outside
useEffect(() => {
@@ -68,7 +74,7 @@ const InlineGroupEdit = ({
window.removeEventListener("scroll", calculateDropdownPosition);
};
}
- }, [isOpen]);
+ }, [isOpen, calculateDropdownPosition]);
const handleEdit = () => {
if (disabled) return;
diff --git a/frontend/src/components/Layout.jsx b/frontend/src/components/Layout.jsx
index 182bd41..3359ab2 100644
--- a/frontend/src/components/Layout.jsx
+++ b/frontend/src/components/Layout.jsx
@@ -29,7 +29,7 @@ import {
Wrench,
X,
} from "lucide-react";
-import React, { useEffect, useRef, useState } from "react";
+import React, { useCallback, useEffect, useRef, useState } from "react";
import { Link, useLocation } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext";
import { useUpdateNotification } from "../contexts/UpdateNotificationContext";
@@ -242,7 +242,7 @@ const Layout = ({ children }) => {
};
// Fetch GitHub stars count
- const fetchGitHubStars = async () => {
+ const fetchGitHubStars = useCallback(async () => {
try {
const response = await fetch(
"https://api.github.com/repos/9technologygroup/patchmon.net",
@@ -254,7 +254,7 @@ const Layout = ({ children }) => {
} catch (error) {
console.error("Failed to fetch GitHub stars:", error);
}
- };
+ }, []);
// Short format for navigation area
const formatRelativeTimeShort = (date) => {
@@ -300,7 +300,7 @@ const Layout = ({ children }) => {
// Fetch GitHub stars on component mount
useEffect(() => {
fetchGitHubStars();
- }, []);
+ }, [fetchGitHubStars]);
return (
diff --git a/frontend/src/contexts/AuthContext.jsx b/frontend/src/contexts/AuthContext.jsx
index 5794e5a..41cb9eb 100644
--- a/frontend/src/contexts/AuthContext.jsx
+++ b/frontend/src/contexts/AuthContext.jsx
@@ -26,41 +26,8 @@ export const AuthProvider = ({ children }) => {
const [checkingSetup, setCheckingSetup] = useState(true);
- // Initialize auth state from localStorage
- useEffect(() => {
- const storedToken = localStorage.getItem("token");
- const storedUser = localStorage.getItem("user");
- const storedPermissions = localStorage.getItem("permissions");
-
- if (storedToken && storedUser) {
- try {
- setToken(storedToken);
- setUser(JSON.parse(storedUser));
- if (storedPermissions) {
- setPermissions(JSON.parse(storedPermissions));
- } else {
- // Fetch permissions if not stored
- fetchPermissions(storedToken);
- }
- } catch (error) {
- console.error("Error parsing stored user data:", error);
- localStorage.removeItem("token");
- localStorage.removeItem("user");
- localStorage.removeItem("permissions");
- }
- }
- setIsLoading(false);
- }, []);
-
- // Refresh permissions when user logs in (no automatic refresh)
- useEffect(() => {
- if (token && user) {
- // Only refresh permissions once when user logs in
- refreshPermissions();
- }
- }, [token, user]);
-
- const fetchPermissions = async (authToken) => {
+ // Define functions first
+ const fetchPermissions = useCallback(async (authToken) => {
try {
setPermissionsLoading(true);
const response = await fetch("/api/v1/permissions/user-permissions", {
@@ -84,15 +51,49 @@ export const AuthProvider = ({ children }) => {
} finally {
setPermissionsLoading(false);
}
- };
+ }, []);
- const refreshPermissions = async () => {
+ const refreshPermissions = useCallback(async () => {
if (token) {
const updatedPermissions = await fetchPermissions(token);
return updatedPermissions;
}
return null;
- };
+ }, [token, fetchPermissions]);
+
+ // Initialize auth state from localStorage
+ useEffect(() => {
+ const storedToken = localStorage.getItem("token");
+ const storedUser = localStorage.getItem("user");
+ const storedPermissions = localStorage.getItem("permissions");
+
+ if (storedToken && storedUser) {
+ try {
+ setToken(storedToken);
+ setUser(JSON.parse(storedUser));
+ if (storedPermissions) {
+ setPermissions(JSON.parse(storedPermissions));
+ } else {
+ // Use the proper fetchPermissions function
+ fetchPermissions(storedToken);
+ }
+ } catch (error) {
+ console.error("Error parsing stored user data:", error);
+ localStorage.removeItem("token");
+ localStorage.removeItem("user");
+ localStorage.removeItem("permissions");
+ }
+ }
+ setIsLoading(false);
+ }, [fetchPermissions]);
+
+ // Refresh permissions when user logs in (no automatic refresh)
+ useEffect(() => {
+ if (token && user) {
+ // Only refresh permissions once when user logs in
+ refreshPermissions();
+ }
+ }, [token, user, refreshPermissions]);
const login = async (username, password) => {
try {
diff --git a/frontend/src/pages/Hosts.jsx b/frontend/src/pages/Hosts.jsx
index f229388..9be58d3 100644
--- a/frontend/src/pages/Hosts.jsx
+++ b/frontend/src/pages/Hosts.jsx
@@ -1199,6 +1199,7 @@ const Hosts = () => {
sortField,
sortDirection,
searchParams,
+ hideStale,
]);
// Group hosts by selected field
diff --git a/frontend/src/pages/Packages.jsx b/frontend/src/pages/Packages.jsx
index 63814e9..e0b75fb 100644
--- a/frontend/src/pages/Packages.jsx
+++ b/frontend/src/pages/Packages.jsx
@@ -186,6 +186,7 @@ const Packages = () => {
securityFilter,
sortField,
sortDirection,
+ hostFilter,
]);
// Get visible columns in order