@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
92 lines (91 loc) • 6.23 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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 Chip from '@mui/material/Chip';
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 List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import Radio from '@mui/material/Radio';
import Typography from '@mui/material/Typography';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { DateLabel } from '../Label';
/**
* RollbackDialog component that shows revision history and allows
* the user to select a specific revision to rollback to.
*/
export default function RollbackDialog(props) {
const { open, resourceKind, resourceName, getRevisionHistory, onClose, onConfirm } = props;
const { t } = useTranslation(['translation']);
const [revisions, setRevisions] = useState([]);
const [selectedRevision, setSelectedRevision] = useState(undefined);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (!open) {
return;
}
let isActive = true;
// eslint-disable-next-line react-hooks/set-state-in-effect
setLoading(true);
setError(null);
setSelectedRevision(undefined);
getRevisionHistory()
.then(history => {
if (!isActive) {
return;
}
setRevisions(history);
const nonCurrent = history.filter(r => !r.isCurrent);
if (nonCurrent.length > 0) {
setSelectedRevision(nonCurrent[0].revision);
}
setLoading(false);
})
.catch(err => {
if (!isActive) {
return;
}
setError(err instanceof Error ? err.message : String(err));
setLoading(false);
});
return () => {
isActive = false;
};
}, [open, getRevisionHistory]);
function handleConfirm() {
onConfirm(selectedRevision);
}
const hasPreviousRevisions = revisions.filter(r => !r.isCurrent).length > 0;
return (_jsxs(Dialog, { open: open, onClose: onClose, maxWidth: "sm", fullWidth: true, children: [_jsx(DialogTitle, { children: t('translation|Rollback {{ kind }}', { kind: resourceKind }) }), _jsxs(DialogContent, { children: [loading && (_jsx(Typography, { variant: "body2", color: "text.secondary", sx: { py: 2 }, children: t('translation|Loading revision history…') })), error && (_jsx(Typography, { variant: "body2", color: "error", sx: { py: 2 }, children: t('translation|Failed to load revision history: {{ error }}', { error }) })), !loading && !error && (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "body2", sx: { mb: 2 }, children: t('translation|Select a revision to rollback "{{ name }}" to. This will replace the current pod template with the one from the selected revision.', { name: resourceName }) }), !hasPreviousRevisions ? (_jsx(Typography, { variant: "body2", color: "text.secondary", children: t('translation|No previous revisions available to rollback to.') })) : (_jsx(List, { sx: { maxHeight: 320, overflow: 'auto' }, children: revisions.map(rev => (_jsxs(ListItemButton, { selected: selectedRevision === rev.revision, onClick: () => {
if (!rev.isCurrent) {
setSelectedRevision(rev.revision);
}
}, disabled: rev.isCurrent, sx: {
borderRadius: 1,
mb: 0.5,
border: '1px solid',
borderColor: selectedRevision === rev.revision ? 'primary.main' : 'divider',
}, children: [_jsx(Radio, { checked: selectedRevision === rev.revision, disabled: rev.isCurrent, size: "small", sx: { mr: 1 } }), _jsx(ListItemText, { primary: _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Typography, { variant: "body2", fontWeight: "medium", children: t('translation|Revision {{ revision }}', {
revision: rev.revision,
}) }), rev.isCurrent && (_jsx(Chip, { label: t('translation|Current'), size: "small", color: "primary", variant: "outlined" }))] }), secondary: _jsxs(Box, { component: "span", children: [_jsx(Typography, { variant: "caption", color: "text.secondary", component: "span", children: _jsx(DateLabel, { date: rev.createdAt }) }), rev.images.length > 0 && (_jsx(Typography, { variant: "caption", color: "text.secondary", component: "span", sx: { display: 'block' }, children: rev.images.map((img, i) => (_jsx(Box, { component: "span", sx: { display: 'block' }, children: img }, i))) }))] }) })] }, rev.revision))) }))] }))] }), _jsxs(DialogActions, { children: [_jsx(Button, { onClick: onClose, children: t('translation|Cancel') }), _jsx(Button, { onClick: handleConfirm, variant: "contained", color: "primary", disabled: !hasPreviousRevisions || selectedRevision === undefined || loading, children: t('translation|Rollback') })] })] }));
}