@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
122 lines (121 loc) • 6.84 kB
JavaScript
import { jsx as _jsx, 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 Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import TextField from '@mui/material/TextField';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { getCluster } from '../../lib/cluster';
import Namespace from '../../lib/k8s/namespace';
import { clusterAction } from '../../redux/clusterActionSlice';
import { EventStatus, HeadlampEventType, useEventCallback } from '../../redux/headlampEventSlice';
import ActionButton from '../common/ActionButton';
import { AuthVisible } from '../common/Resource';
export default function CreateNamespaceButton() {
const { t } = useTranslation(['glossary', 'translation']);
const [namespaceName, setNamespaceName] = useState('');
const [isValidNamespaceName, setIsValidNamespaceName] = useState(false);
const [nameHelperMessage, setNameHelperMessage] = useState('');
const [namespaceDialogOpen, setNamespaceDialogOpen] = useState(false);
const dispatchCreateEvent = useEventCallback(HeadlampEventType.CREATE_RESOURCE);
const dispatch = useDispatch();
function createNewNamespace() {
const clusterData = getCluster();
const newNamespaceData = {
apiVersion: 'v1',
kind: 'Namespace',
metadata: {
name: namespaceName,
},
};
const newNamespaceName = newNamespaceData.metadata.name;
async function namespaceRequest() {
try {
const response = await Namespace.apiEndpoint.post(newNamespaceData, {}, clusterData || '');
setNamespaceName('');
return response;
}
catch (error) {
const statusCode = error?.status;
console.error('Error creating namespace:', error);
if (statusCode === 409) {
setNamespaceDialogOpen(true);
setIsValidNamespaceName(false);
setNameHelperMessage(t('translation|A namespace with this name already exists.'));
}
throw error;
}
}
setNamespaceDialogOpen(false);
dispatch(clusterAction(() => namespaceRequest(), {
startMessage: t('translation|Applying {{ newItemName }}…', {
newItemName: newNamespaceName,
}),
cancelledMessage: t('translation|Cancelled applying {{ newItemName }}.', {
newItemName: newNamespaceName,
}),
successMessage: t('translation|Applied {{ newItemName }}.', {
newItemName: newNamespaceName,
}),
errorMessage: t('translation|Failed to create {{ kind }} {{ name }}.', {
kind: 'namespace',
name: newNamespaceName,
}),
cancelCallback: () => {
setNamespaceDialogOpen(true);
},
}));
}
useEffect(() => {
const isValidNamespaceFormat = Namespace.isValidNamespaceFormat(namespaceName);
setIsValidNamespaceName(isValidNamespaceFormat);
if (!isValidNamespaceFormat) {
if (namespaceName.length > 63) {
setNameHelperMessage(t('translation|Namespaces must be under 64 characters.'));
}
else {
setNameHelperMessage(t("translation|Namespaces must contain only lowercase alphanumeric characters or '-', and must start and end with an alphanumeric character."));
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [namespaceName]);
return (_jsxs(AuthVisible, { item: Namespace, authVerb: "create", children: [_jsx(ActionButton, { "data-testid": "create-namespace-button", color: "primary", description: t('translation|Create Namespace'), icon: 'mdi:plus-circle', onClick: () => {
setNamespaceDialogOpen(true);
} }), _jsxs(Dialog, { "aria-label": "Dialog", open: namespaceDialogOpen, onClose: () => setNamespaceDialogOpen(false), children: [_jsx(DialogTitle, { children: t('translation|Create Namespace') }), _jsx(DialogContent, { children: _jsx(Box, { component: "form", style: { width: '20vw', maxWidth: '20vw' }, children: _jsx(TextField, { margin: "dense", id: "name", "aria-label": "Name", label: t('translation|Name'), type: "text", error: !isValidNamespaceName && namespaceName.length > 0, helperText: !isValidNamespaceName && namespaceName.length > 0 ? nameHelperMessage : '', fullWidth: true, value: namespaceName, onChange: event => setNamespaceName(event.target.value.toLowerCase()), onKeyDown: e => {
if (e.key === 'Enter') {
e.preventDefault();
if (isValidNamespaceName) {
createNewNamespace();
dispatchCreateEvent({
status: EventStatus.CONFIRMED,
});
}
}
} }) }) }), _jsxs(DialogActions, { children: [_jsx(Button, { "data-testid": "create-namespace-dialog-cancel-button", onClick: () => {
setNamespaceDialogOpen(false);
}, children: t('translation|Cancel') }), _jsx(Button, { "data-testid": "create-namespace-dialog-create-button", disabled: !isValidNamespaceName, onClick: () => {
createNewNamespace();
dispatchCreateEvent({
status: EventStatus.CONFIRMED,
});
}, children: t('translation|Create') })] })] })] }));
}