UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

93 lines (92 loc) 4.34 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/EmptyContent'; import { DetailsGrid, SecretField } from '../common/Resource'; import { SectionBox } from '../common/SectionBox'; import { NameValueTable } from '../common/SimpleTable'; function SecretDataSection({ item }) { const { t } = useTranslation(); const dispatch = useDispatch(); const [data, setData] = React.useState(() => _.cloneDeep(item.data || {})); const [isDirty, setIsDirty] = React.useState(false); const lastDataRef = React.useRef(_.cloneDeep(item.data || {})); React.useEffect(() => { const newData = _.cloneDeep(item.data || {}); if (!isDirty && !_.isEqual(newData, lastDataRef.current)) { setData(newData); lastDataRef.current = newData; } }, [item.data, isDirty]); const handleFieldChange = (key, newValue) => { setData(prev => { const nextData = { ...prev, [key]: Base64.encode(newValue) }; setIsDirty(!_.isEqual(nextData, lastDataRef.current)); return nextData; }); }; const handleSave = () => { const updatedSecret = { ...item.jsonData, data }; 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); setIsDirty(false); }; const mainRows = Object.entries(data).map(([key, val]) => ({ name: key, nameID: key, value: (_jsx(SecretField, { value: val, nameID: key, onChange: (e) => handleFieldChange(key, 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') }) })] })) })); } export default function SecretDetails(props) { const params = useParams(); const { name = params.name, namespace = params.namespace, cluster } = props; const { t } = useTranslation(); 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: () => _jsx(SecretDataSection, { item: item }), }, ] })); }