Files
grafana/public/app/features/plugins/admin/components/UpdateAllModalBody.tsx
Hugo Häggmark 2b8c74de2e i18n: removes useTranslate hook (#106556)
* i18n: removes useTranslate hook

* chore: fix duplicate imports

* chore: fix import sorting and hook dependencies
2025-06-12 11:03:52 +02:00

211 lines
6.1 KiB
TypeScript

import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { Checkbox, EmptyState, Icon, Spinner, Tooltip, useStyles2 } from '@grafana/ui';
import { CatalogPlugin } from '../types';
type UpdateError = {
id: string;
message: string;
};
const getStyles = (theme: GrafanaTheme2) => ({
table: css({
marginTop: theme.spacing(2),
width: '100%',
borderCollapse: 'collapse',
}),
tableRow: css({
borderBottom: `1px solid ${theme.colors.border.weak}`,
td: {
paddingRight: theme.spacing(1),
},
}),
icon: css({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}),
header: css({
textAlign: 'left',
padding: theme.spacing(1),
borderBottom: `2px solid ${theme.colors.border.strong}`,
th: {
paddingRight: theme.spacing(1),
},
}),
data: css({
padding: '10px',
}),
footer: css({
fontSize: theme.typography.bodySmall.fontSize,
marginTop: theme.spacing(3),
}),
noPluginsMessage: css({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
}),
tableContainer: css({
overflowY: 'auto',
overflowX: 'hidden',
maxHeight: theme.spacing(41),
marginBottom: theme.spacing(2),
}),
errorIcon: css({
color: theme.colors.error.main,
}),
successIcon: css({
color: theme.colors.success.main,
}),
pluginsInstalled: css({
svg: {
marginRight: theme.spacing(1),
},
}),
});
const StatusIcon = ({
id,
inProgress,
isSelected,
isInstalled,
errorMap,
}: {
id: string;
inProgress: boolean;
isSelected: boolean;
isInstalled: boolean;
errorMap: Map<string, UpdateError>;
}) => {
const styles = useStyles2(getStyles);
if (errorMap && errorMap.has(id)) {
return (
<Tooltip
content={t('plugins.catalog.update-all.error', 'Error updating plugin: {{errorMessage}}', {
errorMessage: errorMap.get(id)?.message,
})}
>
<Icon className={styles.errorIcon} size="xl" name="exclamation-triangle" />
</Tooltip>
);
}
if (isInstalled) {
return <Icon className={styles.successIcon} size="xl" name="check" />;
}
if (inProgress && isSelected) {
return <Spinner />;
}
return '';
};
type Props = {
plugins: CatalogPlugin[];
pluginsNotInstalled: Set<string>;
inProgress: boolean;
selectedPlugins?: Set<string>;
onCheckboxChange: (id: string) => void;
errorMap: Map<string, UpdateError>;
};
export const UpdateModalBody = ({
plugins,
pluginsNotInstalled,
inProgress,
selectedPlugins,
onCheckboxChange,
errorMap,
}: Props) => {
const styles = useStyles2(getStyles);
const numberInstalled = plugins.length - pluginsNotInstalled.size;
const installationFinished = plugins.length !== pluginsNotInstalled.size && !inProgress;
return (
<div>
{plugins.length === 0 ? (
<EmptyState
variant="completed"
message={t('plugins.catalog.update-all.all-plugins-updated', 'All plugins updated!')}
/>
) : (
<>
<div>
<Trans i18nKey="plugins.catalog.update-all.header">The following plugins have update available</Trans>
</div>
<div className={styles.tableContainer}>
<table className={styles.table}>
<thead className={styles.header}>
<tr>
<th>
<Trans i18nKey="plugins.catalog.update-all.update-header">Update</Trans>
</th>
<th>
<Trans i18nKey="plugins.catalog.update-all.name-header">Name</Trans>
</th>
<th>
<Trans i18nKey="plugins.catalog.update-all.installed-header">Installed</Trans>
</th>
<th>
<Trans i18nKey="plugins.catalog.update-all.available-header">Available</Trans>
</th>
<th></th>
</tr>
</thead>
<tbody>
{plugins.map(({ id, name, installedVersion, latestVersion }: CatalogPlugin) => (
<tr key={id} className={styles.tableRow}>
<td>
<Checkbox
onChange={() => onCheckboxChange(id)}
value={selectedPlugins?.has(id)}
disabled={!pluginsNotInstalled.has(id)}
/>
</td>
<td>{name}</td>
<td>{installedVersion}</td>
<td>{latestVersion}</td>
<td className={styles.icon}>
<StatusIcon
id={id}
inProgress={inProgress}
isSelected={selectedPlugins?.has(id) ?? false}
isInstalled={!pluginsNotInstalled.has(id)}
errorMap={errorMap}
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
{numberInstalled > 0 && installationFinished && (
<div className={styles.pluginsInstalled}>
<Icon className={styles.successIcon} size="lg" name="check" />
{`${numberInstalled} ${t('plugins.catalog.update-all.update-status-text', 'plugins updated')}`}
</div>
)}
{errorMap.size > 0 && installationFinished && (
<div className={styles.pluginsInstalled}>
<Icon className={styles.errorIcon} size="lg" name="exclamation-triangle" />
{`${errorMap.size} ${t('plugins.catalog.update-all.error-status-text', 'failed - see error messages')}`}
</div>
)}
{config.pluginAdminExternalManageEnabled && (
<footer className={styles.footer}>
<Trans i18nKey="plugins.catalog.update-all.cloud-update-message">
* It may take a few minutes for the plugins to be available for usage.
</Trans>
</footer>
)}
</>
)}
</div>
);
};