Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2c0c98b
fix(ui): fix Safari badge overflow and align badges to bottom of stat…
PaulJouvanceau Jun 2, 2026
be2b7fd
Delete patch action
PaulJouvanceau Jun 2, 2026
50edb34
refactor(test): reorganize ObjectDetails.test and reduce duplication
PaulJouvanceau Jun 2, 2026
a2f2fea
test(eventSourceManager): refactor test suite for brevity and maintai…
PaulJouvanceau Jun 3, 2026
bd65aef
refactor(tests): rewrite Objects tests for clarity and maintainability
PaulJouvanceau Jun 3, 2026
1dfd20f
Improve test coverage for ActionDialogs
PaulJouvanceau Jun 3, 2026
99046b2
feat: add replace mode to setInstanceStatuses and use it on initial f…
PaulJouvanceau Jun 3, 2026
c74f9b6
feat(ui): highlight destructive actions in red and move them to botto…
PaulJouvanceau Jun 3, 2026
bed549b
fix(ui): increase z-index of all action menus to appear above EventLo…
PaulJouvanceau Jun 3, 2026
26d1b26
feat: sort instances alphabetically in ObjectDetail
PaulJouvanceau Jun 3, 2026
bc0c629
test(App): optimize and fix App.test.jsx coverage
PaulJouvanceau Jun 4, 2026
6a66b28
fix(ui): fix Safari badge overflow and align badges to bottom of stat…
PaulJouvanceau Jun 2, 2026
b5ce925
Delete patch action
PaulJouvanceau Jun 2, 2026
d2aac6f
refactor(test): reorganize ObjectDetails.test and reduce duplication
PaulJouvanceau Jun 2, 2026
3f342e5
test(eventSourceManager): refactor test suite for brevity and maintai…
PaulJouvanceau Jun 3, 2026
617bcdf
refactor(tests): rewrite Objects tests for clarity and maintainability
PaulJouvanceau Jun 3, 2026
81ce94f
Improve test coverage for ActionDialogs
PaulJouvanceau Jun 3, 2026
80ca7c5
feat: add replace mode to setInstanceStatuses and use it on initial f…
PaulJouvanceau Jun 3, 2026
0d2175d
feat(ui): highlight destructive actions in red and move them to botto…
PaulJouvanceau Jun 3, 2026
65ae076
fix(ui): increase z-index of all action menus to appear above EventLo…
PaulJouvanceau Jun 3, 2026
abf571e
feat: sort instances alphabetically in ObjectDetail
PaulJouvanceau Jun 3, 2026
06726d0
test(App): optimize and fix App.test.jsx coverage
PaulJouvanceau Jun 4, 2026
3e4dc70
Merge remote-tracking branch 'origin/dev' into dev
PaulJouvanceau Jun 4, 2026
895fee7
fix(sse): add objectName parameter to startEventReception for path fi…
PaulJouvanceau Jun 4, 2026
4bcb1d4
feat(config, events): reactive config refresh, fallback loading, and …
PaulJouvanceau Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
319 changes: 94 additions & 225 deletions src/components/ClusterStatGrids.jsx

Large diffs are not rendered by default.

49 changes: 36 additions & 13 deletions src/components/ConfigSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import DeleteIcon from "@mui/icons-material/Delete";
import {URL_OBJECT, URL_NODE} from "../config/apiPath.js";
import {parseObjectPath} from "../utils/objectUtils";

const useConfig = (decodedObjectName, configNode, setConfigNode) => {
const useConfig = (decodedObjectName, configNode, setConfigNode, refreshTrigger) => {
const initialState = {
data: null,
loading: false,
Expand All @@ -43,20 +43,23 @@ const useConfig = (decodedObjectName, configNode, setConfigNode) => {
return {...state, loading: false, data: action.payload};
case "FETCH_ERROR":
return {...state, loading: false, error: action.payload};
case "RESET":
return initialState;
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, initialState);
const lastFetch = useRef({});
const fetchConfig = useCallback(async (node) => {

const fetchConfig = useCallback(async (node, forceBypassThrottle = false) => {
if (!node) {
dispatch({type: "FETCH_ERROR", payload: "No node available to fetch configuration."});
dispatch({type: "RESET"});
return;
}
const key = `${decodedObjectName}:${node}`;
const now = Date.now();
if (lastFetch.current[key] && now - lastFetch.current[key] < 1000) return;
if (!forceBypassThrottle && lastFetch.current[key] && now - lastFetch.current[key] < 1000) return;
lastFetch.current[key] = now;
const {namespace, kind, name} = parseObjectPath(decodedObjectName);
const token = localStorage.getItem("authToken") || "";
Expand All @@ -78,13 +81,26 @@ const useConfig = (decodedObjectName, configNode, setConfigNode) => {
dispatch({type: "FETCH_ERROR", payload: `Failed to fetch config: ${err.message}`});
}
}, [decodedObjectName, setConfigNode]);

useEffect(() => {
if (configNode) {
fetchConfig(configNode);
} else {
dispatch({type: "FETCH_ERROR", payload: "No node available to fetch configuration."});
dispatch({type: "RESET"});
}
}, [configNode, decodedObjectName, fetchConfig]);

// Force re-fetch when refreshTrigger bumps (external config change via SSE)
const prevRefreshTrigger = useRef(refreshTrigger);
useEffect(() => {
if (refreshTrigger === prevRefreshTrigger.current) return;
prevRefreshTrigger.current = refreshTrigger;
if (configNode) {
// Bypass throttle so the fresh content is fetched immediately
fetchConfig(configNode, true);
}
}, [refreshTrigger, configNode, fetchConfig]);

return {...state, fetchConfig};
};

Expand Down Expand Up @@ -589,15 +605,17 @@ const ConfigSection = ({
setConfigNode,
openSnackbar,
configDialogOpen,
setConfigDialogOpen
setConfigDialogOpen,
configRefreshTrigger = 0,
}) => {
const theme = useTheme();
const isDark = theme.palette.mode === 'dark';

const {data: configData, loading: configLoading, error: configError, fetchConfig} = useConfig(
decodedObjectName,
configNode,
setConfigNode
setConfigNode,
configRefreshTrigger,
);
const {
data: keywordsData,
Expand Down Expand Up @@ -661,7 +679,7 @@ const ConfigSection = ({
}
openSnackbar("Configuration updated successfully");
if (configNode) {
await fetchConfig(configNode);
await fetchConfig(configNode, true);
setConfigDialogOpen(true);
}
} catch (err) {
Expand Down Expand Up @@ -728,7 +746,7 @@ const ConfigSection = ({
if (successCount > 0) {
openSnackbar(`Successfully added ${successCount} parameter(s)`, "success");
if (configNode) {
await fetchConfig(configNode);
await fetchConfig(configNode, true);
await fetchExistingParams();
setConfigDialogOpen(true);
}
Expand Down Expand Up @@ -773,7 +791,7 @@ const ConfigSection = ({
if (successCount > 0) {
openSnackbar(`Successfully unset ${successCount} parameter(s)`, "success");
if (configNode) {
await fetchConfig(configNode);
await fetchConfig(configNode, true);
await fetchExistingParams();
setConfigDialogOpen(true);
}
Expand Down Expand Up @@ -812,7 +830,7 @@ const ConfigSection = ({
if (successCount > 0) {
openSnackbar(`Successfully deleted ${successCount} section(s)`, "success");
if (configNode) {
await fetchConfig(configNode);
await fetchConfig(configNode, true);
await fetchExistingParams();
setConfigDialogOpen(true);
}
Expand Down Expand Up @@ -840,7 +858,7 @@ const ConfigSection = ({

return (
<Box sx={{mb: 2, width: "100%", display: 'flex', justifyContent: 'flex-end'}}>
<Box sx={{ width: '100%', overflow: 'hidden' }}>
<Box sx={{width: '100%', overflow: 'hidden'}}>
<Button
variant="contained"
size="small"
Expand Down Expand Up @@ -897,9 +915,14 @@ const ConfigSection = ({
</IconButton>
</Tooltip>
</Box>
{!configNode && !configLoading && !configError && (
<Typography color="textSecondary" variant="body2">
No instance selected to view configuration.
</Typography>
)}
{configLoading && <CircularProgress size={24}/>}
{configError && <Alert severity="error" sx={{mb: 2}}>{configError}</Alert>}
{!configLoading && !configError && configData === null && (
{!configLoading && !configError && configData === null && configNode && (
<Typography color="textSecondary" variant="body2">No configuration available.</Typography>
)}
{!configLoading && !configError && configData !== null && (
Expand Down
15 changes: 11 additions & 4 deletions src/components/HeaderSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const HeaderSection = ({
{name: "flip", options: {enabled: true}},
],
sx: {
zIndex: 1300,
zIndex: 10000,
"& .MuiPaper-root": {minWidth: 200, boxShadow: "0px 5px 15px rgba(0,0,0,0.2)"},
},
});
Expand Down Expand Up @@ -129,7 +129,7 @@ const HeaderSection = ({
<Popper open={Boolean(objectMenuAnchor)} anchorEl={objectMenuAnchor} {...popperProps()}>
<ClickAwayListener onClickAway={() => setObjectMenuAnchor(null)}>
<Paper elevation={3} role="menu">
{OBJECT_ACTIONS.map(({name, icon}) => {
{OBJECT_ACTIONS.map(({name, icon, color}) => {
const isAllowed = isActionAllowedForSelection(name, [decodedObjectName]);
return (
<MenuItem
Expand All @@ -140,13 +140,20 @@ const HeaderSection = ({
}}
disabled={!isAllowed || actionInProgress}
sx={{
color: isAllowed ? 'inherit' : 'text.disabled',
color: isAllowed
? (color === "red" ? "error.main" : "inherit")
: "text.disabled",
'&.Mui-disabled': {opacity: 0.5},
}}
aria-label={`Object ${name} action`}
>
<ListItemIcon
sx={{minWidth: 40, color: isAllowed ? 'inherit' : 'text.disabled'}}>
sx={{
minWidth: 40,
color: isAllowed
? (color === "red" ? "error.main" : "inherit")
: "text.disabled"
}}>
{icon}
</ListItemIcon>
<ListItemText>
Expand Down
26 changes: 13 additions & 13 deletions src/components/InstanceCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ import {grey, blue, red} from "@mui/material/colors";
import logger from '../utils/logger.js';

const InstanceCard = ({
node,
nodeData = {},
selectedNodes = [],
toggleNode = () => logger.warn("toggleNode not provided"),
actionInProgress = false,
setIndividualNodeMenuAnchor = () => logger.warn("setIndividualNodeMenuAnchor not provided"),
setCurrentNode = () => logger.warn("setCurrentNode not provided"),
getColor = () => grey[500],
getNodeState = () => ({avail: "unknown", frozen: "unfrozen", state: null}),
instanceName,
onOpenLogs = () => logger.warn("onOpenLogs not provided"),
onViewInstance,
}) => {
node,
nodeData = {},
selectedNodes = [],
toggleNode = () => logger.warn("toggleNode not provided"),
actionInProgress = false,
setIndividualNodeMenuAnchor = () => logger.warn("setIndividualNodeMenuAnchor not provided"),
setCurrentNode = () => logger.warn("setCurrentNode not provided"),
getColor = () => grey[500],
getNodeState = () => ({avail: "unknown", frozen: "unfrozen", state: null}),
instanceName,
onOpenLogs = () => logger.warn("onOpenLogs not provided"),
onViewInstance,
}) => {
const resolvedInstanceName = instanceName || nodeData?.instanceName || nodeData?.name || node;
const [isHovered, setIsHovered] = useState(false);

Expand Down
17 changes: 14 additions & 3 deletions src/components/NodeRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,19 +264,30 @@ const NodeRow = ({
open={Boolean(anchorEl)}
onClose={() => onMenuClose(nodename)}
{...menuProps}
sx={{
...(menuProps.sx || {}),
zIndex: 10000,
}}
>
{filteredMenuItems.map(({name, icon}) => (
{filteredMenuItems.map(({name, icon, color}) => (
<MenuItem
key={name}
onClick={(e) => {
e.stopPropagation();
onAction(nodename, name);
onMenuClose(nodename);
}}
sx={{display: "flex", alignItems: "center", gap: 1}}
sx={{
display: "flex",
alignItems: "center",
gap: 1,
color: color === "red" ? "error.main" : "inherit",
}}
aria-label={`${name} action`}
>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemIcon sx={{color: color === "red" ? "error.main" : "inherit"}}>
{icon}
</ListItemIcon>
<ListItemText>
{name
.split(" ")
Expand Down
12 changes: 9 additions & 3 deletions src/components/NodesTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -425,19 +425,25 @@ const NodesTable = () => {
vertical: "top",
horizontal: "right",
}}
sx={menuSx}
sx={{
...menuSx,
zIndex: 10000,
}}
>
{filteredMenuItems.map(({name, icon}) => (
{filteredMenuItems.map(({name, icon, color}) => (
<MenuItem
key={name}
onClick={() => handleAction(name)}
sx={{
display: "flex",
alignItems: "center",
gap: 1,
color: color === "red" ? "error.main" : "inherit",
}}
>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemIcon sx={{color: color === "red" ? "error.main" : "inherit"}}>
{icon}
</ListItemIcon>
<ListItemText>
{name
.split(" ")
Expand Down
Loading
Loading