123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- import { css } from '@emotion/css';
- import React, { useState } from 'react';
- import { GrafanaTheme2 } from '@grafana/data';
- import {
- Button,
- ClipboardButton,
- DatePickerWithInput,
- Field,
- FieldSet,
- HorizontalGroup,
- Icon,
- Input,
- Label,
- Modal,
- RadioButtonGroup,
- useStyles2,
- } from '@grafana/ui';
- const EXPIRATION_OPTIONS = [
- { label: 'No expiration', value: false },
- { label: 'Set expiration date', value: true },
- ];
- export type ServiceAccountToken = {
- name: string;
- secondsToLive: number;
- };
- interface CreateTokenModalProps {
- isOpen: boolean;
- token: string;
- onCreateToken: (token: ServiceAccountToken) => void;
- onClose: () => void;
- }
- export const CreateTokenModal = ({ isOpen, token, onCreateToken, onClose }: CreateTokenModalProps) => {
- const [newTokenName, setNewTokenName] = useState('');
- const [isWithExpirationDate, setIsWithExpirationDate] = useState(false);
- const [newTokenExpirationDate, setNewTokenExpirationDate] = useState<Date | string>('');
- const [isExpirationDateValid, setIsExpirationDateValid] = useState(false);
- const styles = useStyles2(getStyles);
- const onExpirationDateChange = (value: Date | string) => {
- const isValid = value !== '';
- setIsExpirationDateValid(isValid);
- setNewTokenExpirationDate(value);
- };
- const onCloseInternal = () => {
- setNewTokenName('');
- setIsWithExpirationDate(false);
- setNewTokenExpirationDate('');
- setIsExpirationDateValid(false);
- onClose();
- };
- const modalTitle = (
- <div className={styles.modalHeaderTitle}>
- <Icon className={styles.modalHeaderIcon} name="key-skeleton-alt" size="lg" />
- <span>{!token ? 'Add service account token' : 'Service account token created'}</span>
- </div>
- );
- return (
- <Modal isOpen={isOpen} title={modalTitle} onDismiss={onCloseInternal} className={styles.modal}>
- {!token ? (
- <>
- <FieldSet>
- <Field
- label="Display name"
- description="name to easily identify the token"
- className={styles.modalRow}
- // for now this is required
- // need to make this optional in backend as well
- required={true}
- >
- <Input
- name="tokenName"
- value={newTokenName}
- onChange={(e) => {
- setNewTokenName(e.currentTarget.value);
- }}
- />
- </Field>
- <RadioButtonGroup
- className={styles.modalRow}
- options={EXPIRATION_OPTIONS}
- value={isWithExpirationDate}
- onChange={setIsWithExpirationDate}
- size="md"
- />
- {isWithExpirationDate && (
- <Field label="Expiration date" className={styles.modalRow}>
- <DatePickerWithInput onChange={onExpirationDateChange} value={newTokenExpirationDate} placeholder="" />
- </Field>
- )}
- </FieldSet>
- <Button
- onClick={() =>
- onCreateToken({
- name: newTokenName,
- secondsToLive: getSecondsToLive(newTokenExpirationDate),
- })
- }
- disabled={isWithExpirationDate && !isExpirationDateValid}
- >
- Generate token
- </Button>
- </>
- ) : (
- <>
- <FieldSet>
- <Label
- description="You will not be able to see or generate it again. Loosing a token requires creating new one."
- className={styles.modalRow}
- >
- Copy the token. It will be showed only once.
- </Label>
- <Field label="Token" className={styles.modalRow}>
- <div className={styles.modalTokenRow}>
- <Input name="tokenValue" value={token} readOnly />
- <ClipboardButton
- className={styles.modalCopyToClipboardButton}
- variant="secondary"
- size="md"
- getText={() => token}
- >
- <Icon name="copy" /> Copy to clipboard
- </ClipboardButton>
- </div>
- </Field>
- </FieldSet>
- <HorizontalGroup>
- <ClipboardButton variant="primary" getText={() => token} onClipboardCopy={onCloseInternal}>
- Copy to clipboard and close
- </ClipboardButton>
- <Button variant="secondary" onClick={onCloseInternal}>
- Close
- </Button>
- </HorizontalGroup>
- </>
- )}
- </Modal>
- );
- };
- const getSecondsToLive = (date: Date | string) => {
- const dateAsDate = new Date(date);
- const now = new Date();
- return Math.ceil((dateAsDate.getTime() - now.getTime()) / 1000);
- };
- const getStyles = (theme: GrafanaTheme2) => {
- return {
- modal: css`
- width: 550px;
- `,
- modalRow: css`
- margin-bottom: ${theme.spacing(4)};
- `,
- modalTokenRow: css`
- display: flex;
- `,
- modalCopyToClipboardButton: css`
- margin-left: ${theme.spacing(0.5)};
- `,
- modalHeaderTitle: css`
- font-size: ${theme.typography.size.lg};
- margin: ${theme.spacing(0, 4, 0, 1)};
- display: flex;
- align-items: center;
- position: relative;
- top: 2px;
- `,
- modalHeaderIcon: css`
- margin-right: ${theme.spacing(2)};
- font-size: inherit;
- &:before {
- vertical-align: baseline;
- }
- `,
- };
- };
|