diff --git a/client/modules/User/actions.js b/client/modules/User/actions.js index cfd2664a..f0708c3c 100644 --- a/client/modules/User/actions.js +++ b/client/modules/User/actions.js @@ -222,10 +222,10 @@ export function updateSettings(formValues) { .catch(response => Promise.reject(new Error(response.data.error))); } -export function createApiKeySuccess(token) { +export function createApiKeySuccess(user) { return { type: ActionTypes.API_KEY_CREATED, - token + user }; } @@ -233,9 +233,7 @@ export function createApiKey(label) { return dispatch => axios.post(`${ROOT_URL}/account/api-keys`, { label }, { withCredentials: true }) .then((response) => { - const { token } = response.data; - dispatch(createApiKeySuccess(token)); - return token; + dispatch(createApiKeySuccess(response.data)); }) .catch(response => Promise.reject(new Error(response.data.error))); } diff --git a/client/modules/User/components/APIKeyForm.jsx b/client/modules/User/components/APIKeyForm.jsx index 3d8dcef6..17ea1542 100644 --- a/client/modules/User/components/APIKeyForm.jsx +++ b/client/modules/User/components/APIKeyForm.jsx @@ -1,5 +1,62 @@ import PropTypes from 'prop-types'; import React from 'react'; +import InlineSVG from 'react-inlinesvg'; +import format from 'date-fns/format'; +import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; +import orderBy from 'lodash/orderBy'; + +const trashCan = require('../../../images/trash-can.svg'); + +function NewTokenDisplay({ token }) { + return ( + +

Make sure to copy your new personal access token now. You won’t be able to see it again!

+

+ +
+ ); +} + +function TokenMetadataList({ tokens, onRemove }) { + return ( + + + + + + + + + + + {orderBy(tokens, ['createdAt'], ['desc']).map((v) => { + const keyRow = ( + + + + + + + ); + + const newKeyValue = v.token && ( + + + + ); + + return [keyRow, newKeyValue]; + })} + +
NameCreated onLast usedActions
{v.label}{format(new Date(v.createdAt), 'MMM D, YYYY h:mm A')}{distanceInWordsToNow(new Date(v.lastUsedAt), { addSuffix: true })} + +
+ +
+ ); +} class APIKeyForm extends React.Component { constructor(props) { @@ -7,62 +64,61 @@ class APIKeyForm extends React.Component { this.state = { keyLabel: '' }; this.addKey = this.addKey.bind(this); + this.removeKey = this.removeKey.bind(this); + this.renderApiKeys = this.renderApiKeys.bind(this); } addKey(event) { event.preventDefault(); - document.getElementById('addKeyForm').reset(); - this.props.createApiKey(this.state.keyLabel) - .then(newToken => this.setState({ newToken })); - this.state.keyLabel = ''; + const { keyLabel } = this.state; + + this.setState({ + keyLabel: '' + }); + + this.props.createApiKey(keyLabel); + return false; } - removeKey(keyId) { - this.props.removeApiKey(keyId); + removeKey(key) { + const message = `Are you sure you want to delete "${key.label}"?`; + + if (window.confirm(message)) { + this.props.removeApiKey(key.id); + } + } + + renderApiKeys() { + const hasApiKeys = this.props.apiKeys && this.props.apiKeys.length > 0; + + if (hasApiKeys) { + return ( + + ); + } + return

You have no API keys

; } render() { - const { newToken } = this.state; - - const content = newToken ? - ( -
-

Here is your new key. Copy it somewhere, you won't be able to see it later !

- - -
) : - (
-

Key label

- { this.setState({ keyLabel: event.target.value }); }} - />
- -
- ); - return (
- {content} - - - - {this.props.apiKeys && this.props.apiKeys.map(v => ( - - - - - ))} - -
{v.label}
Created on: {v.createdAt}
Last used on:
{v.lastUsedAt}
+
+ + { this.setState({ keyLabel: event.target.value }); }} + /> + +
+ {this.renderApiKeys()}
); } diff --git a/server/models/user.js b/server/models/user.js index ef0ded8f..8bd33782 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -17,7 +17,9 @@ const apiKeySchema = new Schema({ }, { timestamps: true, _id: true }); apiKeySchema.virtual('publicFields').get(function publicFields() { - return { id: this.id, label: this.label, lastUsedAt: this.lastUsedAt }; + return { + id: this.id, label: this.label, lastUsedAt: this.lastUsedAt, createdAt: this.createdAt + }; }); apiKeySchema.virtual('id').get(function getApiKeyId() {