mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 05:01:50 +08:00

* i18n: removes useTranslate hook * chore: fix duplicate imports * chore: fix import sorting and hook dependencies
211 lines
6.1 KiB
TypeScript
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>
|
|
);
|
|
};
|