Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
327 changes: 184 additions & 143 deletions modules/web/src/component/Dialog/DeploymentDetailDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ import {
Tabs,
Tab,
Box,
Button,
DialogActions,
} from '@mui/material';
import { Deployment } from '@/types/deployment';
import { Pod } from '@/types/pod';
import { useI18n } from '@/hook/useI18n';
import { formatDateTime, formatStatus, formatRelativeTime } from '@/helper/localization';
import DetailDialog from '../DetailDialog';
import YAMLViewerDialog from '../YAMLViewerDialog';
import { useAlert } from '@/hook/useAlert';
import { copyToClipboard } from '@/helper/util';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The copyToClipboard function is imported from @/helper/util, but it doesn't appear to be exported from modules/web/src/helper/util.ts. This will cause a build failure. Please ensure this utility function is correctly implemented and exported from the specified file.


interface TabPanelProps {
children?: React.ReactNode;
Expand Down Expand Up @@ -48,164 +53,200 @@ interface DeploymentDetailDialogProps {

function DeploymentDetailDialog({ open, onClose, data, pods }: DeploymentDetailDialogProps) {
const [tab, setTab] = React.useState(0);

const { t, getCurrentLanguage } = useI18n();
const currentLanguage = getCurrentLanguage();
const [yamlDialogOpen, setYamlDialogOpen] = React.useState(false);
const { success } = useAlert();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To provide feedback to the user when a copy action fails, you should also retrieve the error function from useAlert. This will be used in the error handling for the copy functions.

Suggested change
const { success } = useAlert();
const { success, error } = useAlert();


const displayPods = pods?.
filter((pod) => pod.metadata?.ownerReferences?.[0]?.name?.includes(data?.metadata?.name || '')) || [];


const handleCopyName = async () => {
if (data?.metadata?.name) {
await copyToClipboard(String(data.metadata.name));
success('Copied name');
}
};

const handleCopyUID = async () => {
if (data?.metadata?.uid) {
await copyToClipboard(String(data.metadata.uid));
success('Copied ID');
}
};
Comment on lines +66 to +78

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The handleCopyName and handleCopyUID functions lack error handling for the copyToClipboard async operation, which can lead to unhandled promise rejections. Also, the String() conversion is unnecessary.

This suggestion adds try...catch blocks for robust error handling. For further improvement, consider refactoring these two similar functions into a single generic handler to reduce code duplication.

  const handleCopyName = async () => {
    if (data?.metadata?.name) {
      try {
        await copyToClipboard(data.metadata.name);
        success('Copied name');
      } catch (err) {
        console.error('Failed to copy name:', err);
        error('Failed to copy name');
      }
    }
  };

  const handleCopyUID = async () => {
    if (data?.metadata?.uid) {
      try {
        await copyToClipboard(data.metadata.uid);
        success('Copied ID');
      } catch (err) {
        console.error('Failed to copy ID:', err);
        error('Failed to copy ID');
      }
    }
  };


return (
<DetailDialog
open={open}
onClose={onClose}
data={data}
title={`${t("common.deployment")} ${t("actions.detail")}`}
>
<Grid container spacing={2}>
<Grid item xs={4}>
<TextField
label={t('table.namespace')}
value={data?.metadata?.namespace}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t('table.name')}
value={data?.metadata?.name}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
<>
<DetailDialog
open={open}
onClose={onClose}
data={data}
title={`${t("common.deployment")} ${t("actions.detail")}`}
>
<Grid container spacing={2}>
<Grid item xs={4}>
<TextField
label={t('table.namespace')}
value={data?.metadata?.namespace}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t('table.name')}
value={data?.metadata?.name}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t('table.id')}
value={data?.metadata?.uid}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
<Grid item xs={4}>
<TextField
label={`${t('table.replicas')} (${t('table.availableReplicas')}/${t('table.unavailableReplicas')})`}
value={`${data?.status?.availableReplicas || 0}/${(data?.status?.replicas || 0) - (data?.status?.availableReplicas || 0)}`}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t('table.creationTime')}
value={formatDateTime(data?.metadata?.creationTimestamp || '', currentLanguage)}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
</Grid>
<Grid item xs={4}>
<TextField
label={t('table.id')}
value={data?.metadata?.uid}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
<Grid item xs={4}>
<TextField
label={`${t('table.replicas')} (${t('table.availableReplicas')}/${t('table.unavailableReplicas')})`}
value={`${data?.status?.availableReplicas || 0}/${(data?.status?.replicas || 0) - (data?.status?.availableReplicas || 0)}`}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t('table.creationTime')}
value={formatDateTime(data?.metadata?.creationTimestamp || '', currentLanguage)}
fullWidth
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
</Grid>
</Grid>

<Box sx={{ marginTop: 2 }}>
<Tabs value={tab} onChange={(_, value) => setTab(value)}>
<Tab label={t('table.pods')} />
<Tab label={t('table.labels')} />
</Tabs>
</Box>

<TabPanel value={tab} index={0}>
<Table sx={{ marginTop: 2 }}>
<TableHead>
<TableRow>
<TableCell>{`${t('table.name')}/${t('table.id')}`}</TableCell>
<TableCell>{t('table.node')}</TableCell>
<TableCell>{t('table.status')}</TableCell>
<TableCell>{t('table.resource')}</TableCell>
<TableCell>{t('table.creationTime')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{(displayPods?.length || 0) > 0 ? (
displayPods?.map((pod) => (
<TableRow key={pod?.metadata?.uid}>
<TableCell>
<div>
<div style={{ color: 'rgb(47, 84, 235)', marginBottom: '2px' }}>
{pod?.metadata?.name}

<Box sx={{ marginTop: 2 }}>
<Tabs value={tab} onChange={(_, value) => setTab(value)}>
<Tab label={t('table.pods')} />
<Tab label={t('table.labels')} />
</Tabs>
</Box>

<TabPanel value={tab} index={0}>
<Table sx={{ marginTop: 2 }}>
<TableHead>
<TableRow>
<TableCell>{`${t('table.name')}/${t('table.id')}`}</TableCell>
<TableCell>{t('table.node')}</TableCell>
<TableCell>{t('table.status')}</TableCell>
<TableCell>{t('table.resource')}</TableCell>
<TableCell>{t('table.creationTime')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{(displayPods?.length || 0) > 0 ? (
displayPods?.map((pod) => (
<TableRow key={pod?.metadata?.uid}>
<TableCell>
<div>
<div style={{ color: 'rgb(47, 84, 235)', marginBottom: '2px' }}>
{pod?.metadata?.name}
</div>
<div>{pod?.metadata?.uid}</div>
</div>
<div>{pod?.metadata?.uid}</div>
</div>
</TableCell>
<TableCell>{pod?.spec?.nodeName}</TableCell>
<TableCell>{formatStatus(pod?.status?.phase, currentLanguage)}</TableCell>
<TableCell>
<div>
<div style={{ fontSize: "12px" }}>{t('table.cpu')}: {pod?.spec?.containers?.at(0)?.resources?.requests?.cpu}</div>
<div style={{ fontSize: "12px" }}>
{t('table.memory')}: {pod?.spec?.containers?.at(0)?.resources?.requests?.memory}
</TableCell>
<TableCell>{pod?.spec?.nodeName}</TableCell>
<TableCell>{formatStatus(pod?.status?.phase, currentLanguage)}</TableCell>
<TableCell>
<div>
<div style={{ fontSize: "12px" }}>{t('table.cpu')}: {pod?.spec?.containers?.at(0)?.resources?.requests?.cpu}</div>
<div style={{ fontSize: "12px" }}>
{t('table.memory')}: {pod?.spec?.containers?.at(0)?.resources?.requests?.memory}
</div>
</div>
</div>
</TableCell>
<TableCell>
<div style={{ fontSize: '12px' }}>
{formatDateTime(pod?.metadata?.creationTimestamp || '', currentLanguage)}
</div>
<div style={{ fontSize: '12px', color: 'text.secondary' }}>
{formatRelativeTime(pod?.metadata?.creationTimestamp || '', currentLanguage)}
</div>
</TableCell>
<TableCell>
<div style={{ fontSize: '12px' }}>
{formatDateTime(pod?.metadata?.creationTimestamp || '', currentLanguage)}
</div>
<div style={{ fontSize: '12px', color: 'text.secondary' }}>
{formatRelativeTime(pod?.metadata?.creationTimestamp || '', currentLanguage)}
</div>
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={5} align="center">
No data
</TableCell>
</TableRow>
))
) : (
)}
</TableBody>
</Table>
</TabPanel>

<TabPanel value={tab} index={1}>
<Table sx={{ marginTop: 2 }}>
<TableHead>
<TableRow>
<TableCell colSpan={4} align="center">
No data
</TableCell>
<TableCell>{t('table.key')}</TableCell>
<TableCell>{t('table.value')}</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TabPanel>
<TabPanel value={tab} index={1}>
<Table sx={{ marginTop: 2 }}>
<TableHead>
<TableRow>
<TableCell>{t('table.key')}</TableCell>
<TableCell>{t('table.value')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{(Object.keys(data?.metadata?.labels || {})?.length || 0) > 0 ? (
Object.entries(data?.metadata?.labels || {})?.map((pair) => (
<TableRow key={pair[0]}>
<TableCell>{pair[0]}</TableCell>
<TableCell>{pair[1]}</TableCell>
</TableHead>
<TableBody>
{(Object.keys(data?.metadata?.labels || {})?.length || 0) > 0 ? (
Object.entries(data?.metadata?.labels || {})?.map((pair) => (
<TableRow key={pair[0]}>
<TableCell>{pair[0]}</TableCell>
<TableCell>{pair[1]}</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={2} align="center">
No data
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={4} align="center">
No data
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TabPanel>
</DetailDialog>
)}
</TableBody>
</Table>
</TabPanel>

<DialogActions>
<Button onClick={onClose}>{t('actions.cancel')}</Button>
<Button onClick={handleCopyName} variant="outlined">Copy Name</Button>
<Button onClick={handleCopyUID} variant="outlined">Copy ID</Button>
<Button onClick={() => setYamlDialogOpen(true)} variant="contained">
YAML
</Button>
</DialogActions>
</DetailDialog>

<YAMLViewerDialog
open={yamlDialogOpen}
onClose={() => setYamlDialogOpen(false)}
content={data}
/>
</>
);
}

Expand Down
Loading