diff --git a/frontend/src/component/admin/api-token/ApiTokenCreate/ApiTokenCreate.tsx b/frontend/src/component/admin/api-token/ApiTokenCreate/ApiTokenCreate.tsx deleted file mode 100644 index 90bf6abb53..0000000000 --- a/frontend/src/component/admin/api-token/ApiTokenCreate/ApiTokenCreate.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import { TextField } from '@material-ui/core'; -import classNames from 'classnames'; -import React, { useState, useEffect } from 'react'; -import { styles as commonStyles } from '../../../common'; -import { IApiTokenCreate } from '../../../../hooks/api/actions/useApiTokensApi/useApiTokensApi'; -import useEnvironments from '../../../../hooks/api/getters/useEnvironments/useEnvironments'; -import useProjects from '../../../../hooks/api/getters/useProjects/useProjects'; -import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; -import ConditionallyRender from '../../../common/ConditionallyRender'; -import Dialogue from '../../../common/Dialogue'; -import GeneralSelect from '../../../common/GeneralSelect/GeneralSelect'; - -import { useStyles } from './styles'; - -const ALL = '*'; -const TYPE_ADMIN = 'ADMIN'; -const TYPE_CLIENT = 'CLIENT'; - -interface IApiTokenCreateProps { - showDialog: boolean; - closeDialog: () => void; - createToken: (token: IApiTokenCreate) => Promise; -} - -interface IDataError { - username?: string; - general?: string; -} - -const INITIAL_DATA: IApiTokenCreate = { - username: '', - type: TYPE_CLIENT, - project: ALL, -}; - -const ApiTokenCreate = ({ - showDialog, - closeDialog, - createToken, -}: IApiTokenCreateProps) => { - const styles = useStyles(); - const [data, setData] = useState(INITIAL_DATA); - const [error, setError] = useState({}); - const { projects } = useProjects(); - const { environments } = useEnvironments(); - const { uiConfig } = useUiConfig(); - - useEffect(() => { - if ( - environments && - environments.length > 0 && - data.type === TYPE_CLIENT && - !data.environment - ) { - setData({ ...data, environment: environments[0].name }); - } - }, [data, environments]); - - const clear = () => { - const environment = - environments && environments.length > 0 - ? environments[0].name - : undefined; - setData({ ...INITIAL_DATA, environment }); - setError({}); - }; - - const onCancel = (e: Event) => { - clear(); - closeDialog(); - }; - - const isValid = () => { - if (!data.username) { - setError({ username: 'Username is required.' }); - return false; - } else { - setError({}); - return true; - } - }; - - const submit = async () => { - if (!isValid()) { - return; - } - - try { - await createToken(data); - clear(); - closeDialog(); - } catch (error) { - setError({ general: 'Unable to create new API token' }); - } - }; - - const setType = (event: React.ChangeEvent<{ value: string }>) => { - const value = event.target.value; - if (value === TYPE_ADMIN) { - setData({ ...data, type: value, environment: ALL, project: ALL }); - } else { - setData({ - ...data, - type: value, - environment: environments[0].name, - }); - } - }; - - const setUsername = (event: React.ChangeEvent<{ value: string }>) => { - const value = event.target.value; - setData({ ...data, username: value }); - }; - - const setProject = (event: React.ChangeEvent<{ value: string }>) => { - const value = event.target.value; - setData({ ...data, project: value }); - }; - - const setEnvironment = (event: React.ChangeEvent<{ value: string }>) => { - const value = event.target.value; - setData({ ...data, environment: value }); - }; - - const selectableProjects = [{ id: '*', name: 'ALL' }, ...projects].map( - i => ({ - key: i.id, - label: i.name, - title: i.name, - }) - ); - - const selectableEnvs = - data.type === TYPE_ADMIN - ? [{ key: '*', label: 'ALL' }] - : environments.map(i => ({ - key: i.name, - label: i.name, - title: i.name, - })); - - const selectableTypes = [ - { key: 'CLIENT', label: 'Client', title: 'Client SDK token' }, - { key: 'ADMIN', label: 'Admin', title: 'Admin API token' }, - ]; - - const formId = 'create-api-token-form'; - - return ( - submit()} - open={showDialog} - onClose={onCancel} - primaryButtonText="Create" - secondaryButtonText="Cancel" - title="New API token" - formId={formId} - > -
- - - - - - - } /> - -
- ); -}; - -export default ApiTokenCreate; diff --git a/frontend/src/component/admin/api-token/ApiTokenCreate/styles.js b/frontend/src/component/admin/api-token/ApiTokenCreate/styles.js deleted file mode 100644 index cb194c5274..0000000000 --- a/frontend/src/component/admin/api-token/ApiTokenCreate/styles.js +++ /dev/null @@ -1,8 +0,0 @@ -import { makeStyles } from '@material-ui/styles'; - -export const useStyles = makeStyles({ - addApiKeyForm: { - display: 'flex', - flexDirection: 'column', - }, -}); diff --git a/frontend/src/component/admin/api-token/ApiTokenForm/ApiTokenForm.styles.ts b/frontend/src/component/admin/api-token/ApiTokenForm/ApiTokenForm.styles.ts new file mode 100644 index 0000000000..72bb60a9aa --- /dev/null +++ b/frontend/src/component/admin/api-token/ApiTokenForm/ApiTokenForm.styles.ts @@ -0,0 +1,53 @@ +import { makeStyles } from '@material-ui/core/styles'; + +export const useStyles = makeStyles(theme => ({ + container: { + maxWidth: '400px', + }, + form: { + display: 'flex', + flexDirection: 'column', + height: '100%', + }, + input: { width: '100%', marginBottom: '1rem' }, + selectInput: { + marginBottom: '1rem', + minWidth: '400px', + [theme.breakpoints.down(600)]: { + minWidth: '379px', + }, + }, + label: { + minWidth: '300px', + [theme.breakpoints.down(600)]: { + minWidth: 'auto', + }, + }, + buttonContainer: { + marginTop: 'auto', + display: 'flex', + justifyContent: 'flex-end', + }, + cancelButton: { + marginRight: '1.5rem', + }, + inputDescription: { + marginBottom: '0.5rem', + }, + formHeader: { + fontWeight: 'normal', + marginTop: '0', + }, + header: { + fontWeight: 'normal', + }, + permissionErrorContainer: { + position: 'relative', + }, + errorMessage: { + fontSize: theme.fontSizes.smallBody, + color: theme.palette.error.main, + position: 'absolute', + top: '-8px', + }, +})); diff --git a/frontend/src/component/admin/api-token/ApiTokenForm/ApiTokenForm.tsx b/frontend/src/component/admin/api-token/ApiTokenForm/ApiTokenForm.tsx new file mode 100644 index 0000000000..6394466303 --- /dev/null +++ b/frontend/src/component/admin/api-token/ApiTokenForm/ApiTokenForm.tsx @@ -0,0 +1,142 @@ +import { Button } from '@material-ui/core'; +import { KeyboardArrowDownOutlined } from '@material-ui/icons'; +import React from 'react'; +import useEnvironments from '../../../../hooks/api/getters/useEnvironments/useEnvironments'; +import useProjects from '../../../../hooks/api/getters/useProjects/useProjects'; +import GeneralSelect from '../../../common/GeneralSelect/GeneralSelect'; +import Input from '../../../common/Input/Input'; +import PermissionButton from '../../../common/PermissionButton/PermissionButton'; +import { ADMIN } from '../../../providers/AccessProvider/permissions'; +import { useStyles } from './ApiTokenForm.styles'; + +interface IApiTokenFormProps { + username: string; + type: string; + project: string; + environment: string; + setTokenType: (value: string) => void; + setUsername: React.Dispatch>; + setProject: React.Dispatch>; + setEnvironment: React.Dispatch>; + handleSubmit: (e: any) => void; + handleCancel: () => void; + errors: { [key: string]: string }; + submitButtonText: string; + clearErrors: () => void; +} +const ApiTokenForm = ({ + username, + type, + project, + environment, + setUsername, + setTokenType, + setProject, + setEnvironment, + handleSubmit, + handleCancel, + errors, + clearErrors, + submitButtonText, +}: IApiTokenFormProps) => { + const TYPE_ADMIN = 'ADMIN'; + const styles = useStyles(); + const { environments } = useEnvironments(); + const { projects } = useProjects(); + + const selectableTypes = [ + { key: 'CLIENT', label: 'Client', title: 'Client SDK token' }, + { key: 'ADMIN', label: 'Admin', title: 'Admin API token' }, + ]; + + const selectableProjects = [{ id: '*', name: 'ALL' }, ...projects].map( + i => ({ + key: i.id, + label: i.name, + title: i.name, + }) + ); + const selectableEnvs = + type === TYPE_ADMIN + ? [{ key: '*', label: 'ALL' }] + : environments.map(i => ({ + key: i.name, + label: i.name, + title: i.name, + })); + + return ( +
+

Token information

+ +
+

+ Who are you generating the token for? +

+ setUsername(e.target.value)} + label="Username" + error={errors.username !== undefined} + errorText={errors.username} + onFocus={() => clearErrors()} + /> +

+ What is your token type? +

+ setTokenType(e.target.value as string)} + label="Token Type" + id="api_key_type" + name="type" + IconComponent={KeyboardArrowDownOutlined} + className={styles.selectInput} + /> +

+ Which project do you want to give access to? +

+ setProject(e.target.value as string)} + label="Project" + IconComponent={KeyboardArrowDownOutlined} + className={styles.selectInput} + /> +

+ Which environment should the token have access to? +

+ setEnvironment(e.target.value as string)} + label="Environment" + id="api_key_environment" + name="environment" + IconComponent={KeyboardArrowDownOutlined} + className={styles.selectInput} + /> +
+
+ + + {submitButtonText} token + +
+
+ ); +}; + +export default ApiTokenForm; diff --git a/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx b/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx index 40c9c9afbf..cbdfc0a8e7 100644 --- a/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx +++ b/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx @@ -1,5 +1,5 @@ import { useContext, useState } from 'react'; -import { Link } from 'react-router-dom'; +import { Link, useHistory } from 'react-router-dom'; import { Button, IconButton, @@ -14,9 +14,7 @@ import useToast from '../../../../hooks/useToast'; import useLoading from '../../../../hooks/useLoading'; import useApiTokens from '../../../../hooks/api/getters/useApiTokens/useApiTokens'; import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; -import useApiTokensApi, { - IApiTokenCreate, -} from '../../../../hooks/api/actions/useApiTokensApi/useApiTokensApi'; +import useApiTokensApi from '../../../../hooks/api/actions/useApiTokensApi/useApiTokensApi'; import ApiError from '../../../common/ApiError/ApiError'; import PageContent from '../../../common/PageContent'; import HeaderTitle from '../../../common/HeaderTitle'; @@ -29,7 +27,6 @@ import { useStyles } from './ApiTokenList.styles'; import { formatDateWithLocale } from '../../../common/util'; import Secret from './secret'; import { Delete, FileCopy } from '@material-ui/icons'; -import ApiTokenCreate from '../ApiTokenCreate/ApiTokenCreate'; import Dialogue from '../../../common/Dialogue'; import { CREATE_API_TOKEN_BUTTON } from '../../../../testIds'; import { Alert } from '@material-ui/lab'; @@ -54,20 +51,11 @@ const ApiTokenList = ({ location }: IApiTokenList) => { const { uiConfig } = useUiConfig(); const [showDelete, setShowDelete] = useState(false); const [delToken, setDeleteToken] = useState(); - const { setToastData, setToastApiError } = useToast(); + const { setToastData } = useToast(); const { tokens, loading, refetch, error } = useApiTokens(); - const { deleteToken, createToken } = useApiTokensApi(); + const { deleteToken } = useApiTokensApi(); const ref = useLoading(loading); - - const [showDialog, setDialog] = useState(false); - - const openDialog = () => { - setDialog(true); - }; - - const closeDialog = () => { - setDialog(false); - }; + const history = useHistory(); const renderError = () => { return ( @@ -79,26 +67,11 @@ const ApiTokenList = ({ location }: IApiTokenList) => { ); }; - const onCreateToken = async (token: IApiTokenCreate) => { - try { - await createToken(token); - refetch(); - setToastData({ - type: 'success', - title: 'Created token', - text: 'Successfully created API token', - confetti: true, - }); - } catch (e) { - setToastApiError(e.message); - } - }; const copyToken = (value: string) => { if (copy(value)) { setToastData({ type: 'success', title: 'Token copied', - confetti: true, text: `Token is copied to clipboard`, }); } @@ -271,7 +244,11 @@ const ApiTokenList = ({ location }: IApiTokenList) => {