UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

121 lines (120 loc) 7.09 kB
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) })); })] })); }