diff --git a/client/modules/User/components/APIKeyForm.jsx b/client/modules/User/components/APIKeyForm.jsx
index a0da81b6..17ec61e7 100644
--- a/client/modules/User/components/APIKeyForm.jsx
+++ b/client/modules/User/components/APIKeyForm.jsx
@@ -1,67 +1,19 @@
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');
+import CopyableInput from '../../IDE/components/CopyableInput';
+import APIKeyList from './APIKeyList';
-function NewTokenDisplay({ token }) {
- function handleCopyClick() {
- navigator.clipboard.writeText(token);
- }
+const plusIcon = require('../../../images/plus-icon.svg');
- 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 (
-
You have no exsiting tokens.
;
}
render() {
+ const keyWithToken = this.props.apiKeys.find(k => !!k.token);
+
return (
-
-
- {this.renderApiKeys()}
+
+
Personal Access Tokens act like your password to allow automated scripts to access the Editor API. Create a token for each script that needs access.
+
+
+
Create new token
+
+
+ {
+ keyWithToken && (
+
+
Your new access token
+
Make sure to copy your new personal access token now. You won’t be able to see it again!
+
+
+ )
+ }
+
+
+
+
Existing tokens
+ {this.renderApiKeys()}
+
);
}
}
APIKeyForm.propTypes = {
+ apiKeys: PropTypes.arrayOf(PropTypes.shape(APIKeyPropType)).isRequired,
createApiKey: PropTypes.func.isRequired,
removeApiKey: PropTypes.func.isRequired,
- apiKeys: PropTypes.arrayOf(PropTypes.shape({
- id: PropTypes.object.isRequired,
- label: PropTypes.string.isRequired,
- createdAt: PropTypes.object.isRequired,
- lastUsedAt: PropTypes.object.isRequired,
- })).isRequired
};
export default APIKeyForm;
diff --git a/client/modules/User/components/APIKeyList.jsx b/client/modules/User/components/APIKeyList.jsx
new file mode 100644
index 00000000..b3df65dd
--- /dev/null
+++ b/client/modules/User/components/APIKeyList.jsx
@@ -0,0 +1,50 @@
+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';
+
+import { APIKeyPropType } from './APIKeyForm';
+
+const trashCan = require('../../../images/trash-can.svg');
+
+function APIKeyList({ apiKeys, onRemove }) {
+ return (
+
+
+
+ Name |
+ Created on |
+ Last used |
+ Actions |
+
+
+
+ {orderBy(apiKeys, ['createdAt'], ['desc']).map((key) => {
+ const hasNewToken = !!key.token;
+
+ return (
+
+ {key.label} |
+ {format(new Date(key.createdAt), 'MMM D, YYYY h:mm A')} |
+ {distanceInWordsToNow(new Date(key.lastUsedAt), { addSuffix: true })} |
+
+
+ |
+
+ );
+ })}
+
+
+ );
+}
+
+APIKeyList.propTypes = {
+ apiKeys: PropTypes.arrayOf(PropTypes.shape(APIKeyPropType)).isRequired,
+ onRemove: PropTypes.func.isRequired,
+};
+
+export default APIKeyList;
diff --git a/client/modules/User/pages/AccountView.jsx b/client/modules/User/pages/AccountView.jsx
index 9cf18eb8..0da5a37d 100644
--- a/client/modules/User/pages/AccountView.jsx
+++ b/client/modules/User/pages/AccountView.jsx
@@ -16,7 +16,6 @@ import APIKeyForm from '../components/APIKeyForm';
const exitUrl = require('../../../images/exit.svg');
const logoUrl = require('../../../images/p5js-logo.svg');
-
class AccountView extends React.Component {
constructor(props) {
super(props);
@@ -38,7 +37,7 @@ class AccountView extends React.Component {
render() {
return (
-
+
p5.js Web Editor | Account
@@ -52,7 +51,7 @@ class AccountView extends React.Component {