|
1 | 1 | import PropTypes from 'prop-types'; |
2 | | -import React from 'react'; |
| 2 | +import React, { useState } from 'react'; |
| 3 | +import { useTranslation } from 'react-i18next'; |
| 4 | +import { useDispatch, useSelector } from 'react-redux'; |
3 | 5 | import Button from '../../../common/Button'; |
4 | 6 | import { PlusIcon } from '../../../common/icons'; |
5 | 7 | import CopyableInput from '../../IDE/components/CopyableInput'; |
| 8 | +import { createApiKey, removeApiKey } from '../actions'; |
6 | 9 |
|
7 | 10 | import APIKeyList from './APIKeyList'; |
8 | 11 |
|
9 | 12 | export const APIKeyPropType = PropTypes.shape({ |
10 | | - id: PropTypes.objectOf(PropTypes.shape()).isRequired, |
11 | | - token: PropTypes.objectOf(PropTypes.shape()), |
| 13 | + id: PropTypes.string.isRequired, |
| 14 | + token: PropTypes.string, |
12 | 15 | label: PropTypes.string.isRequired, |
13 | 16 | createdAt: PropTypes.string.isRequired, |
14 | 17 | lastUsedAt: PropTypes.string |
15 | 18 | }); |
16 | 19 |
|
17 | | -class APIKeyForm extends React.Component { |
18 | | - constructor(props) { |
19 | | - super(props); |
20 | | - this.state = { keyLabel: '' }; |
| 20 | +const APIKeyForm = () => { |
| 21 | + const { t } = useTranslation(); |
| 22 | + const apiKeys = useSelector((state) => state.user.apiKeys); |
| 23 | + const dispatch = useDispatch(); |
21 | 24 |
|
22 | | - this.addKey = this.addKey.bind(this); |
23 | | - this.removeKey = this.removeKey.bind(this); |
24 | | - this.renderApiKeys = this.renderApiKeys.bind(this); |
25 | | - } |
| 25 | + const [keyLabel, setKeyLabel] = useState(''); |
26 | 26 |
|
27 | | - addKey(event) { |
| 27 | + const addKey = (event) => { |
28 | 28 | event.preventDefault(); |
29 | | - const { keyLabel } = this.state; |
| 29 | + dispatch(createApiKey(keyLabel)); |
| 30 | + setKeyLabel(''); |
| 31 | + }; |
30 | 32 |
|
31 | | - this.setState({ |
32 | | - keyLabel: '' |
33 | | - }); |
34 | | - |
35 | | - this.props.createApiKey(keyLabel); |
36 | | - |
37 | | - return false; |
38 | | - } |
39 | | - |
40 | | - removeKey(key) { |
41 | | - const message = this.props.t('APIKeyForm.ConfirmDelete', { |
| 33 | + const removeKey = (key) => { |
| 34 | + const message = t('APIKeyForm.ConfirmDelete', { |
42 | 35 | key_label: key.label |
43 | 36 | }); |
44 | 37 |
|
45 | 38 | if (window.confirm(message)) { |
46 | | - this.props.removeApiKey(key.id); |
| 39 | + dispatch(removeApiKey(key.id)); |
47 | 40 | } |
48 | | - } |
| 41 | + }; |
49 | 42 |
|
50 | | - renderApiKeys() { |
51 | | - const hasApiKeys = this.props.apiKeys && this.props.apiKeys.length > 0; |
| 43 | + const renderApiKeys = () => { |
| 44 | + const hasApiKeys = apiKeys && apiKeys.length > 0; |
52 | 45 |
|
53 | 46 | if (hasApiKeys) { |
54 | | - return ( |
55 | | - <APIKeyList apiKeys={this.props.apiKeys} onRemove={this.removeKey} /> |
56 | | - ); |
| 47 | + return <APIKeyList apiKeys={apiKeys} onRemove={removeKey} />; |
57 | 48 | } |
58 | | - return <p>{this.props.t('APIKeyForm.NoTokens')}</p>; |
59 | | - } |
60 | | - |
61 | | - render() { |
62 | | - const keyWithToken = this.props.apiKeys.find((k) => !!k.token); |
63 | | - |
64 | | - return ( |
65 | | - <div className="api-key-form"> |
66 | | - <p className="api-key-form__summary"> |
67 | | - {this.props.t('APIKeyForm.Summary')} |
68 | | - </p> |
69 | | - |
70 | | - <div className="api-key-form__section"> |
71 | | - <h3 className="api-key-form__title"> |
72 | | - {this.props.t('APIKeyForm.CreateToken')} |
73 | | - </h3> |
74 | | - <form className="form form--inline" onSubmit={this.addKey}> |
75 | | - <label |
76 | | - htmlFor="keyLabel" |
77 | | - className="form__label form__label--hidden " |
78 | | - > |
79 | | - {this.props.t('APIKeyForm.TokenLabel')} |
80 | | - </label> |
81 | | - <input |
82 | | - className="form__input" |
83 | | - id="keyLabel" |
84 | | - onChange={(event) => { |
85 | | - this.setState({ keyLabel: event.target.value }); |
86 | | - }} |
87 | | - placeholder={this.props.t('APIKeyForm.TokenPlaceholder')} |
88 | | - type="text" |
89 | | - value={this.state.keyLabel} |
| 49 | + return <p>{t('APIKeyForm.NoTokens')}</p>; |
| 50 | + }; |
| 51 | + |
| 52 | + const keyWithToken = apiKeys.find((k) => !!k.token); |
| 53 | + |
| 54 | + return ( |
| 55 | + <div className="api-key-form"> |
| 56 | + <p className="api-key-form__summary">{t('APIKeyForm.Summary')}</p> |
| 57 | + |
| 58 | + <div className="api-key-form__section"> |
| 59 | + <h3 className="api-key-form__title">{t('APIKeyForm.CreateToken')}</h3> |
| 60 | + <form className="form form--inline" onSubmit={addKey}> |
| 61 | + <label |
| 62 | + htmlFor="keyLabel" |
| 63 | + className="form__label form__label--hidden " |
| 64 | + > |
| 65 | + {t('APIKeyForm.TokenLabel')} |
| 66 | + </label> |
| 67 | + <input |
| 68 | + className="form__input" |
| 69 | + id="keyLabel" |
| 70 | + onChange={(event) => { |
| 71 | + setKeyLabel(event.target.value); |
| 72 | + }} |
| 73 | + placeholder={t('APIKeyForm.TokenPlaceholder')} |
| 74 | + type="text" |
| 75 | + value={keyLabel} |
| 76 | + /> |
| 77 | + <Button |
| 78 | + disabled={keyLabel === ''} |
| 79 | + iconBefore={<PlusIcon />} |
| 80 | + label="Create new key" |
| 81 | + type="submit" |
| 82 | + > |
| 83 | + {t('APIKeyForm.CreateTokenSubmit')} |
| 84 | + </Button> |
| 85 | + </form> |
| 86 | + |
| 87 | + {keyWithToken && ( |
| 88 | + <div className="api-key-form__new-token"> |
| 89 | + <h4 className="api-key-form__new-token__title"> |
| 90 | + {t('APIKeyForm.NewTokenTitle')} |
| 91 | + </h4> |
| 92 | + <p className="api-key-form__new-token__info"> |
| 93 | + {t('APIKeyForm.NewTokenInfo')} |
| 94 | + </p> |
| 95 | + <CopyableInput |
| 96 | + label={keyWithToken.label} |
| 97 | + value={keyWithToken.token} |
90 | 98 | /> |
91 | | - <Button |
92 | | - disabled={this.state.keyLabel === ''} |
93 | | - iconBefore={<PlusIcon />} |
94 | | - label="Create new key" |
95 | | - type="submit" |
96 | | - > |
97 | | - {this.props.t('APIKeyForm.CreateTokenSubmit')} |
98 | | - </Button> |
99 | | - </form> |
100 | | - |
101 | | - {keyWithToken && ( |
102 | | - <div className="api-key-form__new-token"> |
103 | | - <h4 className="api-key-form__new-token__title"> |
104 | | - {this.props.t('APIKeyForm.NewTokenTitle')} |
105 | | - </h4> |
106 | | - <p className="api-key-form__new-token__info"> |
107 | | - {this.props.t('APIKeyForm.NewTokenInfo')} |
108 | | - </p> |
109 | | - <CopyableInput |
110 | | - label={keyWithToken.label} |
111 | | - value={keyWithToken.token} |
112 | | - /> |
113 | | - </div> |
114 | | - )} |
115 | | - </div> |
116 | | - |
117 | | - <div className="api-key-form__section"> |
118 | | - <h3 className="api-key-form__title"> |
119 | | - {this.props.t('APIKeyForm.ExistingTokensTitle')} |
120 | | - </h3> |
121 | | - {this.renderApiKeys()} |
122 | | - </div> |
| 99 | + </div> |
| 100 | + )} |
123 | 101 | </div> |
124 | | - ); |
125 | | - } |
126 | | -} |
127 | 102 |
|
128 | | -APIKeyForm.propTypes = { |
129 | | - apiKeys: PropTypes.arrayOf(PropTypes.shape(APIKeyPropType)).isRequired, |
130 | | - createApiKey: PropTypes.func.isRequired, |
131 | | - removeApiKey: PropTypes.func.isRequired, |
132 | | - t: PropTypes.func.isRequired |
| 103 | + <div className="api-key-form__section"> |
| 104 | + <h3 className="api-key-form__title"> |
| 105 | + {t('APIKeyForm.ExistingTokensTitle')} |
| 106 | + </h3> |
| 107 | + {renderApiKeys()} |
| 108 | + </div> |
| 109 | + </div> |
| 110 | + ); |
133 | 111 | }; |
134 | 112 |
|
135 | 113 | export default APIKeyForm; |
0 commit comments