UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

88 lines (87 loc) 4.79 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 Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import { Base64 } from 'js-base64'; import _ from 'lodash'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { useParams } from 'react-router-dom'; import Secret from '../../lib/k8s/secret'; import { clusterAction } from '../../redux/clusterActionSlice'; import { EmptyContent } from '../common'; import { DetailsGrid, SecretField } from '../common/Resource'; import { SectionBox } from '../common/SectionBox'; import { NameValueTable } from '../common/SimpleTable'; export default function SecretDetails(props) { const params = useParams(); const { name = params.name, namespace = params.namespace, cluster } = props; const { t } = useTranslation(); const dispatch = useDispatch(); return (_jsx(DetailsGrid, { resourceType: Secret, name: name, namespace: namespace, cluster: cluster, withEvents: true, extraInfo: item => item && [ { name: t('translation|Type'), value: item.type, }, ], extraSections: item => item && [ { id: 'headlamp.secrets-data', section: () => { const initialData = _.mapValues(item.data, (v) => Base64.decode(v)); const [data, setData] = React.useState(initialData); const lastDataRef = React.useRef(initialData); React.useEffect(() => { const newData = _.mapValues(item.data, (v) => Base64.decode(v)); if (!_.isEqual(newData, lastDataRef.current)) { if (_.isEqual(data, lastDataRef.current)) { setData(newData); lastDataRef.current = newData; } } }, [item.data]); const handleFieldChange = (key, newValue) => { setData(prev => ({ ...prev, [key]: newValue })); }; const handleSave = () => { const encodedData = _.mapValues(data, (v) => Base64.encode(v)); const updatedSecret = { ...item.jsonData, data: encodedData }; dispatch(clusterAction(() => item.update(updatedSecret), { startMessage: t('translation|Applying changes to {{ itemName }}…', { itemName: item.metadata.name, }), cancelledMessage: t('translation|Cancelled changes to {{ itemName }}.', { itemName: item.metadata.name, }), successMessage: t('translation|Applied changes to {{ itemName }}.', { itemName: item.metadata.name, }), errorMessage: t('translation|Failed to apply changes to {{ itemName }}.', { itemName: item.metadata.name, }), })); lastDataRef.current = _.cloneDeep(data); }; const mainRows = Object.entries(data).map((item) => ({ name: item[0], value: (_jsx(SecretField, { value: item[1], onChange: e => handleFieldChange(item[0], e.target.value) })), })); return (_jsx(SectionBox, { title: t('translation|Data'), children: mainRows.length === 0 ? (_jsx(EmptyContent, { children: t('No data in this secret') })) : (_jsxs(_Fragment, { children: [_jsx(NameValueTable, { rows: mainRows }), _jsx(Box, { mt: 2, display: "flex", justifyContent: "flex-end", children: _jsx(Button, { variant: "contained", color: "primary", onClick: handleSave, children: t('translation|Save') }) })] })) })); }, }, ] })); }