@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
121 lines (120 loc) • 7.09 kB
JavaScript
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
/*
* Copyright 2025 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Icon } from '@iconify/react';
import { Box, DialogContentText } from '@mui/material';
import IconButton from '@mui/material/IconButton';
import ListItemText from '@mui/material/ListItemText';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Tooltip from '@mui/material/Tooltip';
import { useSnackbar } from 'notistack';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import helpers from '../../../helpers';
import { deleteCluster } from '../../../lib/k8s/api/v1/clusterApi';
import { createRouteURL } from '../../../lib/router/createRouteURL';
import { useId } from '../../../lib/util';
import { setConfig } from '../../../redux/configSlice';
import { useTypedSelector } from '../../../redux/hooks';
import { ConfirmDialog } from '../../common/ConfirmDialog';
import ErrorBoundary from '../../common/ErrorBoundary/ErrorBoundary';
/**
* ClusterContextMenu component displays a context menu for a given cluster.
*/
export default function ClusterContextMenu({ cluster }) {
const { t } = useTranslation(['translation']);
const history = useHistory();
const dispatch = useDispatch();
const [anchorEl, setAnchorEl] = React.useState(null);
const menuId = useId('context-menu');
const [openConfirmDialog, setOpenConfirmDialog] = React.useState(null);
const dialogs = useTypedSelector(state => state.clusterProvider.dialogs);
const menuItems = useTypedSelector(state => state.clusterProvider.menuItems);
const isDynamicClusterEnabled = useTypedSelector(state => state.config.isDynamicClusterEnabled);
const allowKubeconfigChanges = useTypedSelector(state => state.config.allowKubeconfigChanges);
const { enqueueSnackbar } = useSnackbar();
const kubeconfigOrigin = cluster.meta_data?.origin?.kubeconfig;
const deleteFromKubeconfig = cluster.meta_data?.source === 'kubeconfig';
function removeCluster(cluster) {
const clusterID = cluster.meta_data?.clusterID;
const originalName = cluster.meta_data?.originalName ?? '';
const clusterName = cluster.name;
deleteCluster(clusterName, deleteFromKubeconfig, clusterID, kubeconfigOrigin, originalName)
.then(config => {
dispatch(setConfig(config));
})
.catch((err) => {
enqueueSnackbar(t('translation|Failed to delete cluster: {{ error }}', { error: err.message }), {
variant: 'error',
preventDuplicate: true,
});
})
.finally(() => {
history.push('/');
});
}
function removeClusterDescription(cluster) {
const description = deleteFromKubeconfig
? t('translation|This action will delete cluster "{{ clusterName }}" from "{{ source }}"', {
clusterName: cluster.name,
source: kubeconfigOrigin,
})
: t('translation|This action will remove cluster "{{ clusterName }}".', {
clusterName: cluster.name,
});
const removeFromKubeconfigDes = deleteFromKubeconfig
? t('translation|This action cannot be undone! Do you want to proceed?')
: t('translation|Remove this cluster?');
return (_jsxs(_Fragment, { children: [description, removeFromKubeconfigDes && (_jsx(Box, { sx: {
display: 'flex',
alignItems: 'center',
marginTop: '1rem',
marginBottom: '1rem',
}, children: _jsx(DialogContentText, { id: "alert-dialog-description", children: removeFromKubeconfigDes }) }))] }));
}
function handleMenuClose() {
setAnchorEl(null);
}
return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: t('Actions'), children: _jsx(IconButton, { size: "small", onClick: event => {
setAnchorEl(event.currentTarget);
}, "aria-haspopup": "menu", "aria-controls": menuId, "aria-label": t('Actions'), children: _jsx(Icon, { icon: "mdi:more-vert" }) }) }), _jsxs(Menu, { id: menuId, anchorEl: anchorEl, open: Boolean(anchorEl), onClose: () => {
handleMenuClose();
}, children: [_jsx(MenuItem, { onClick: () => {
history.push(createRouteURL('cluster', { cluster: cluster.name }));
handleMenuClose();
}, children: _jsx(ListItemText, { children: t('translation|View') }) }), _jsx(MenuItem, { onClick: () => {
history.push(createRouteURL('settingsCluster', { cluster: cluster.name }));
handleMenuClose();
}, children: _jsx(ListItemText, { children: t('translation|Settings') }) }), (!menuItems || menuItems.length === 0) &&
((cluster.meta_data?.source === 'dynamic_cluster' &&
(helpers.isElectron() || isDynamicClusterEnabled)) ||
(cluster.meta_data?.source === 'kubeconfig' &&
(helpers.isElectron() || allowKubeconfigChanges))) && (_jsx(MenuItem, { onClick: () => {
setOpenConfirmDialog('deleteDynamic');
handleMenuClose();
}, children: _jsx(ListItemText, { children: t('translation|Delete') }) })), menuItems.map((Item, index) => {
return (_jsx(Item, { cluster: cluster, setOpenConfirmDialog: setOpenConfirmDialog, handleMenuClose: handleMenuClose }, index));
})] }), _jsx(ConfirmDialog, { open: openConfirmDialog === 'deleteDynamic', handleClose: () => setOpenConfirmDialog(''), confirmLabel: t('translation|Delete'), onConfirm: () => {
setOpenConfirmDialog('');
removeCluster(cluster);
}, title: t('translation|Delete Cluster'), description: removeClusterDescription(cluster) }), openConfirmDialog !== null &&
dialogs.map((Dialog, index) => {
return (_jsx(ErrorBoundary, { children: _jsx(Dialog, { cluster: cluster, openConfirmDialog: openConfirmDialog, setOpenConfirmDialog: setOpenConfirmDialog }, index) }));
})] }));
}