1
- import React , { PureComponent } from 'react' ;
1
+ import React , { PureComponent } from 'react' ;
2
2
import ReactDOMServer from 'react-dom/server' ;
3
3
import { connect } from 'react-redux' ;
4
4
import { hot } from 'react-hot-loader' ;
5
5
import { NavModel , ApiKey , NewApiKey , OrgRole } from 'app/types' ;
6
6
import { getNavModel } from 'app/core/selectors/navModel' ;
7
- import { getApiKeys } from './state/selectors' ;
7
+ import { getApiKeys , getApiKeysCount } from './state/selectors' ;
8
8
import { loadApiKeys , deleteApiKey , setSearchQuery , addApiKey } from './state/actions' ;
9
9
import PageHeader from 'app/core/components/PageHeader/PageHeader' ;
10
- import SlideDown from 'app/core/components/Animations/SlideDown' ;
11
10
import PageLoader from 'app/core/components/PageLoader/PageLoader' ;
11
+ import SlideDown , { defaultStyle as slideDownDefaultStyle } from 'app/core/components/Animations/SlideDown' ;
12
12
import ApiKeysAddedModal from './ApiKeysAddedModal' ;
13
13
import config from 'app/core/config' ;
14
14
import appEvents from 'app/core/app_events' ;
15
+ import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA' ;
16
+ import DeleteButton from 'app/core/components/DeleteButton/DeleteButton' ;
15
17
16
18
export interface Props {
17
19
navModel : NavModel ;
@@ -22,6 +24,7 @@ export interface Props {
22
24
deleteApiKey : typeof deleteApiKey ;
23
25
setSearchQuery : typeof setSearchQuery ;
24
26
addApiKey : typeof addApiKey ;
27
+ apiKeysCount : number ;
25
28
}
26
29
27
30
export interface State {
@@ -101,115 +104,147 @@ export class ApiKeysPage extends PureComponent<Props, any> {
101
104
} ) ;
102
105
} ;
103
106
104
- renderTable ( ) {
105
- const { apiKeys } = this . props ;
106
-
107
- return [
108
- < h3 key = "header" className = "page-heading" >
109
- Existing Keys
110
- </ h3 > ,
111
- < table key = "table" className = "filter-table" >
112
- < thead >
113
- < tr >
114
- < th > Name</ th >
115
- < th > Role</ th >
116
- < th style = { { width : '34px' } } />
117
- </ tr >
118
- </ thead >
119
- { apiKeys . length > 0 && (
120
- < tbody >
121
- { apiKeys . map ( key => {
122
- return (
123
- < tr key = { key . id } >
124
- < td > { key . name } </ td >
125
- < td > { key . role } </ td >
126
- < td >
127
- < a onClick = { ( ) => this . onDeleteApiKey ( key ) } className = "btn btn-danger btn-mini" >
128
- < i className = "fa fa-remove" />
129
- </ a >
130
- </ td >
131
- </ tr >
132
- ) ;
133
- } ) }
134
- </ tbody >
107
+ renderEmptyList ( ) {
108
+ const { isAdding } = this . state ;
109
+ return (
110
+ < div className = "page-container page-body" >
111
+ { ! isAdding && (
112
+ < EmptyListCTA
113
+ model = { {
114
+ title : "You haven't added any API Keys yet." ,
115
+ buttonIcon : 'fa fa-plus' ,
116
+ buttonLink : '#' ,
117
+ onClick : this . onToggleAdding ,
118
+ buttonTitle : ' New API Key' ,
119
+ proTip : 'Remember you can provide view-only API access to other applications.' ,
120
+ proTipLink : '' ,
121
+ proTipLinkTitle : '' ,
122
+ proTipTarget : '_blank' ,
123
+ } }
124
+ />
135
125
) }
136
- </ table > ,
137
- ] ;
126
+ { this . renderAddApiKeyForm ( ) }
127
+ </ div >
128
+ ) ;
138
129
}
139
130
140
- render ( ) {
131
+ renderAddApiKeyForm ( ) {
141
132
const { newApiKey, isAdding } = this . state ;
142
- const { hasFetched , navModel , searchQuery } = this . props ;
133
+ const slideDownStyle = isAdding ? slideDownDefaultStyle : { ... slideDownDefaultStyle , transition : 'unset' } ;
143
134
144
135
return (
145
- < div >
146
- < PageHeader model = { navModel } />
147
- < div className = "page-container page-body" >
148
- < div className = "page-action-bar" >
149
- < div className = "gf-form gf-form--grow" >
150
- < label className = "gf-form--has-input-icon gf-form--grow" >
136
+ < SlideDown in = { isAdding } style = { slideDownStyle } >
137
+ < div className = "cta-form" >
138
+ < button className = "cta-form__close btn btn-transparent" onClick = { this . onToggleAdding } >
139
+ < i className = "fa fa-close" />
140
+ </ button >
141
+ < h5 > Add API Key</ h5 >
142
+ < form className = "gf-form-group" onSubmit = { this . onAddApiKey } >
143
+ < div className = "gf-form-inline" >
144
+ < div className = "gf-form max-width-21" >
145
+ < span className = "gf-form-label" > Key name</ span >
151
146
< input
152
147
type = "text"
153
148
className = "gf-form-input"
154
- placeholder = "Search keys"
155
- value = { searchQuery }
156
- onChange = { this . onSearchQueryChange }
149
+ value = { newApiKey . name }
150
+ placeholder = "Name"
151
+ onChange = { evt => this . onApiKeyStateUpdate ( evt , ApiKeyStateProps . Name ) }
157
152
/>
158
- < i className = "gf-form-input-icon fa fa-search" />
159
- </ label >
153
+ </ div >
154
+ < div className = "gf-form" >
155
+ < span className = "gf-form-label" > Role</ span >
156
+ < span className = "gf-form-select-wrapper" >
157
+ < select
158
+ className = "gf-form-input gf-size-auto"
159
+ value = { newApiKey . role }
160
+ onChange = { evt => this . onApiKeyStateUpdate ( evt , ApiKeyStateProps . Role ) }
161
+ >
162
+ { Object . keys ( OrgRole ) . map ( role => {
163
+ return (
164
+ < option key = { role } label = { role } value = { role } >
165
+ { role }
166
+ </ option >
167
+ ) ;
168
+ } ) }
169
+ </ select >
170
+ </ span >
171
+ </ div >
172
+ < div className = "gf-form" >
173
+ < button className = "btn gf-form-btn btn-success" > Add</ button >
174
+ </ div >
160
175
</ div >
176
+ </ form >
177
+ </ div >
178
+ </ SlideDown >
179
+ ) ;
180
+ }
161
181
162
- < div className = "page-action-bar__spacer" />
163
- < button className = "btn btn-success pull-right" onClick = { this . onToggleAdding } disabled = { isAdding } >
164
- < i className = "fa fa-plus" /> Add API Key
165
- </ button >
182
+ renderApiKeyList ( ) {
183
+ const { isAdding } = this . state ;
184
+ const { apiKeys, searchQuery } = this . props ;
185
+
186
+ return (
187
+ < div className = "page-container page-body" >
188
+ < div className = "page-action-bar" >
189
+ < div className = "gf-form gf-form--grow" >
190
+ < label className = "gf-form--has-input-icon gf-form--grow" >
191
+ < input
192
+ type = "text"
193
+ className = "gf-form-input"
194
+ placeholder = "Search keys"
195
+ value = { searchQuery }
196
+ onChange = { this . onSearchQueryChange }
197
+ />
198
+ < i className = "gf-form-input-icon fa fa-search" />
199
+ </ label >
166
200
</ div >
167
201
168
- < SlideDown in = { isAdding } >
169
- < div className = "cta-form" >
170
- < button className = "cta-form__close btn btn-transparent" onClick = { this . onToggleAdding } >
171
- < i className = "fa fa-close" />
172
- </ button >
173
- < h5 > Add API Key</ h5 >
174
- < form className = "gf-form-group" onSubmit = { this . onAddApiKey } >
175
- < div className = "gf-form-inline" >
176
- < div className = "gf-form max-width-21" >
177
- < span className = "gf-form-label" > Key name</ span >
178
- < input
179
- type = "text"
180
- className = "gf-form-input"
181
- value = { newApiKey . name }
182
- placeholder = "Name"
183
- onChange = { evt => this . onApiKeyStateUpdate ( evt , ApiKeyStateProps . Name ) }
184
- />
185
- </ div >
186
- < div className = "gf-form" >
187
- < span className = "gf-form-label" > Role</ span >
188
- < span className = "gf-form-select-wrapper" >
189
- < select
190
- className = "gf-form-input gf-size-auto"
191
- value = { newApiKey . role }
192
- onChange = { evt => this . onApiKeyStateUpdate ( evt , ApiKeyStateProps . Role ) }
193
- >
194
- { Object . keys ( OrgRole ) . map ( role => {
195
- return (
196
- < option key = { role } label = { role } value = { role } >
197
- { role }
198
- </ option >
199
- ) ;
200
- } ) }
201
- </ select >
202
- </ span >
203
- </ div >
204
- < div className = "gf-form" >
205
- < button className = "btn gf-form-btn btn-success" > Add</ button >
206
- </ div >
207
- </ div >
208
- </ form >
209
- </ div >
210
- </ SlideDown >
211
- { hasFetched ? this . renderTable ( ) : < PageLoader pageName = "Api keys" /> }
202
+ < div className = "page-action-bar__spacer" />
203
+ < button className = "btn btn-success pull-right" onClick = { this . onToggleAdding } disabled = { isAdding } >
204
+ < i className = "fa fa-plus" /> Add API Key
205
+ </ button >
212
206
</ div >
207
+
208
+ { this . renderAddApiKeyForm ( ) }
209
+
210
+ < h3 className = "page-heading" > Existing Keys</ h3 >
211
+ < table className = "filter-table" >
212
+ < thead >
213
+ < tr >
214
+ < th > Name</ th >
215
+ < th > Role</ th >
216
+ < th style = { { width : '34px' } } />
217
+ </ tr >
218
+ </ thead >
219
+ { apiKeys . length > 0 ? (
220
+ < tbody >
221
+ { apiKeys . map ( key => {
222
+ return (
223
+ < tr key = { key . id } >
224
+ < td > { key . name } </ td >
225
+ < td > { key . role } </ td >
226
+ < td >
227
+ < DeleteButton onConfirmDelete = { ( ) => this . onDeleteApiKey ( key ) } />
228
+ </ td >
229
+ </ tr >
230
+ ) ;
231
+ } ) }
232
+ </ tbody >
233
+ ) : null }
234
+ </ table >
235
+ </ div >
236
+ ) ;
237
+ }
238
+
239
+ render ( ) {
240
+ const { hasFetched, navModel, apiKeysCount } = this . props ;
241
+
242
+ return (
243
+ < div >
244
+ < PageHeader model = { navModel } />
245
+ { hasFetched ?
246
+ ( apiKeysCount > 0 ? this . renderApiKeyList ( ) : this . renderEmptyList ( ) )
247
+ : < PageLoader pageName = "Api keys" /> }
213
248
</ div >
214
249
) ;
215
250
}
@@ -220,7 +255,8 @@ function mapStateToProps(state) {
220
255
navModel : getNavModel ( state . navIndex , 'apikeys' ) ,
221
256
apiKeys : getApiKeys ( state . apiKeys ) ,
222
257
searchQuery : state . apiKeys . searchQuery ,
223
- hasFetched : state . apiKeys . hasFetched ,
258
+ apiKeysCount : getApiKeysCount ( state . apiKeys ) ,
259
+ hasFetched : state . apiKeys . hasFetched
224
260
} ;
225
261
}
226
262
0 commit comments