grafana/public/app/features/serviceaccounts/ServiceAccountTokensTable.tsx
Eric Leijonmarck c6943797f9
Admin: token expiration colors and calculations (#45231)
* token expiration colorign and calculations

* Update public/app/features/serviceaccounts/ServiceAccountTokensTable.tsx

* removed unused calculation for expiry

* optional attribute

* fix: typo

* implement failing test :thumpsup:

* tests

* refactor: tests to use assertify

* tiem

* refactor: remote porntf

* refactor: make test NOT sleep 1 sec for all builds :D

Co-authored-by: J Guerreiro <joao.guerreiro@grafana.com>
2022-02-18 11:43:33 +01:00

115 lines
3.2 KiB
TypeScript

import React, { FC } from 'react';
import { DeleteButton, Icon, Tooltip, useStyles2, useTheme2 } from '@grafana/ui';
import { dateTimeFormat, GrafanaTheme2, TimeZone } from '@grafana/data';
import { ApiKey } from '../../types';
import { css } from '@emotion/css';
interface Props {
tokens: ApiKey[];
timeZone: TimeZone;
onDelete: (token: ApiKey) => void;
}
export const ServiceAccountTokensTable: FC<Props> = ({ tokens, timeZone, onDelete }) => {
const theme = useTheme2();
const styles = getStyles(theme);
return (
<>
<table className="filter-table">
<thead>
<tr>
<th>Name</th>
<th>Expires</th>
<th>Created</th>
<th style={{ width: '34px' }} />
</tr>
</thead>
<tbody>
{tokens.map((key) => {
return (
<tr key={key.id} className={styles.tableRow(key.hasExpired)}>
<td>{key.name}</td>
<td>
<TokenExpiration timeZone={timeZone} token={key} />
</td>
<td>{formatDate(timeZone, key.created)}</td>
<td>
<DeleteButton aria-label="Delete API key" size="sm" onConfirm={() => onDelete(key)} />
</td>
</tr>
);
})}
</tbody>
</table>
</>
);
};
function formatDate(timeZone: TimeZone, expiration?: string): string {
if (!expiration) {
return 'No expiration date';
}
return dateTimeFormat(expiration, { timeZone });
}
function formatSecondsLeftUntilExpiration(secondsUntilExpiration: number): string {
const days = Math.floor(secondsUntilExpiration / (3600 * 24));
const daysFormat = days > 1 ? `${days} days` : `${days} day`;
return `Expires in ${daysFormat}`;
}
interface TokenExpirationProps {
timeZone: TimeZone;
token: ApiKey;
}
const TokenExpiration = ({ timeZone, token }: TokenExpirationProps) => {
const styles = useStyles2(getStyles);
if (!token.expiration) {
return <span className={styles.neverExpire}>Never</span>;
}
if (token.secondsUntilExpiration) {
return (
<span className={styles.secondsUntilExpiration}>
{formatSecondsLeftUntilExpiration(token.secondsUntilExpiration)}
</span>
);
}
if (token.hasExpired) {
return (
<span className={styles.hasExpired}>
Expired
<span className={styles.tooltipContainer}>
<Tooltip content="This API key has expired.">
<Icon name="exclamation-triangle" className={styles.toolTipIcon} />
</Tooltip>
</span>
</span>
);
}
return <span>{formatDate(timeZone, token.expiration)}</span>;
};
const getStyles = (theme: GrafanaTheme2) => ({
tableRow: (hasExpired: boolean | undefined) => css`
color: ${hasExpired ? theme.colors.text.secondary : theme.colors.text.primary};
`,
tooltipContainer: css`
margin-left: ${theme.spacing(1)};
`,
toolTipIcon: css`
color: ${theme.colors.error.text};
`,
secondsUntilExpiration: css`
color: ${theme.colors.warning.text};
`,
hasExpired: css`
color: ${theme.colors.error.text};
`,
neverExpire: css`
color: ${theme.colors.text.secondary};
`,
});