@@ -9,6 +9,7 @@ import { ActionTypes } from 'lib/stores/ConfigStore';
9
9
import Button from 'components/Button/Button.react' ;
10
10
import ConfigDialog from 'dashboard/Data/Config/ConfigDialog.react' ;
11
11
import DeleteParameterDialog from 'dashboard/Data/Config/DeleteParameterDialog.react' ;
12
+ import AddArrayEntryDialog from 'dashboard/Data/Config/AddArrayEntryDialog.react' ;
12
13
import EmptyState from 'components/EmptyState/EmptyState.react' ;
13
14
import Icon from 'components/Icon/Icon.react' ;
14
15
import { isDate } from 'lib/DateUtils' ;
@@ -20,9 +21,11 @@ import TableHeader from 'components/Table/TableHeader.react';
20
21
import TableView from 'dashboard/TableView.react' ;
21
22
import Toolbar from 'components/Toolbar/Toolbar.react' ;
22
23
import browserStyles from 'dashboard/Data/Browser/Browser.scss' ;
24
+ import configStyles from 'dashboard/Data/Config/Config.scss' ;
23
25
import { CurrentApp } from 'context/currentApp' ;
24
26
import Modal from 'components/Modal/Modal.react' ;
25
27
import equal from 'fast-deep-equal' ;
28
+ import Notification from 'dashboard/Data/Browser/Notification.react' ;
26
29
27
30
@subscribeTo ( 'Config' , 'config' )
28
31
class Config extends TableView {
@@ -41,7 +44,12 @@ class Config extends TableView {
41
44
modalMasterKeyOnly : false ,
42
45
loading : false ,
43
46
confirmModalOpen : false ,
47
+ lastError : null ,
48
+ lastNote : null ,
49
+ showAddEntryDialog : false ,
50
+ addEntryParam : '' ,
44
51
} ;
52
+ this . noteTimeout = null ;
45
53
}
46
54
47
55
componentWillMount ( ) {
@@ -107,6 +115,15 @@ class Config extends TableView {
107
115
onConfirm = { this . deleteParam . bind ( this , this . state . modalParam ) }
108
116
/>
109
117
) ;
118
+ } else if ( this . state . showAddEntryDialog ) {
119
+ extras = (
120
+ < AddArrayEntryDialog
121
+ onCancel = { this . closeAddEntryDialog . bind ( this ) }
122
+ onConfirm = { value =>
123
+ this . addArrayEntry ( this . state . addEntryParam , value )
124
+ }
125
+ />
126
+ ) ;
110
127
}
111
128
112
129
if ( this . state . confirmModalOpen ) {
@@ -127,12 +144,24 @@ class Config extends TableView {
127
144
} }
128
145
>
129
146
< div className = { [ browserStyles . confirmConfig ] } >
130
- This parameter changed while you were editing it. If you continue, the latest changes will be lost and replaced with your version. Do you want to proceed?
147
+ This parameter changed while you were editing it. If you continue, the latest changes
148
+ will be lost and replaced with your version. Do you want to proceed?
131
149
</ div >
132
150
</ Modal >
133
151
) ;
134
152
}
135
- return extras ;
153
+ let notification = null ;
154
+ if ( this . state . lastError ) {
155
+ notification = < Notification note = { this . state . lastError } isErrorNote = { true } /> ;
156
+ } else if ( this . state . lastNote ) {
157
+ notification = < Notification note = { this . state . lastNote } isErrorNote = { false } /> ;
158
+ }
159
+ return (
160
+ < >
161
+ { extras }
162
+ { notification }
163
+ </ >
164
+ ) ;
136
165
}
137
166
138
167
parseValueForModal ( dataValue ) {
@@ -186,7 +215,6 @@ class Config extends TableView {
186
215
* Opens the modal dialog to edit the Config parameter.
187
216
*/
188
217
const openModal = async ( ) => {
189
-
190
218
// Show dialog
191
219
this . setState ( {
192
220
loading : true ,
@@ -203,7 +231,8 @@ class Config extends TableView {
203
231
// Get latest param values
204
232
const fetchedParams = this . props . config . data . get ( 'params' ) ;
205
233
const fetchedValue = fetchedParams . get ( this . state . modalParam ) ;
206
- const fetchedMasterKeyOnly = this . props . config . data . get ( 'masterKeyOnly' ) ?. get ( this . state . modalParam ) || false ;
234
+ const fetchedMasterKeyOnly =
235
+ this . props . config . data . get ( 'masterKeyOnly' ) ?. get ( this . state . modalParam ) || false ;
207
236
208
237
// Parse fetched data
209
238
const { modalValue : fetchedModalValue } = this . parseValueForModal ( fetchedValue ) ;
@@ -219,6 +248,8 @@ class Config extends TableView {
219
248
// Define column styles
220
249
const columnStyleLarge = { width : '30%' , cursor : 'pointer' } ;
221
250
const columnStyleSmall = { width : '15%' , cursor : 'pointer' } ;
251
+ const columnStyleValue = { width : '25%' , cursor : 'pointer' } ;
252
+ const columnStyleAction = { width : '10%' } ;
222
253
223
254
const openModalValueColumn = ( ) => {
224
255
if ( data . value instanceof Parse . File ) {
@@ -241,13 +272,23 @@ class Config extends TableView {
241
272
< td style = { columnStyleSmall } onClick = { openModal } >
242
273
{ type }
243
274
</ td >
244
- < td style = { columnStyleLarge } onClick = { openModalValueColumn } >
275
+ < td style = { columnStyleValue } onClick = { openModalValueColumn } >
245
276
{ value }
246
277
</ td >
278
+ < td style = { columnStyleAction } >
279
+ { type === 'Array' && (
280
+ < a
281
+ className = { configStyles . configActionIcon }
282
+ onClick = { ( ) => this . openAddEntryDialog ( data . param ) }
283
+ >
284
+ < Icon width = { 18 } height = { 18 } name = "plus-solid" />
285
+ </ a >
286
+ ) }
287
+ </ td >
247
288
< td style = { columnStyleSmall } onClick = { openModal } >
248
289
{ data . masterKeyOnly . toString ( ) }
249
290
</ td >
250
- < td style = { { textAlign : 'center' } } >
291
+ < td style = { { textAlign : 'center' , width : '5%' } } >
251
292
< a onClick = { openDeleteParameterDialog } >
252
293
< Icon width = { 16 } height = { 16 } name = "trash-solid" fill = "#ff395e" />
253
294
</ a >
@@ -264,9 +305,12 @@ class Config extends TableView {
264
305
< TableHeader key = "type" width = { 15 } >
265
306
Type
266
307
</ TableHeader > ,
267
- < TableHeader key = "value" width = { 30 } >
308
+ < TableHeader key = "value" width = { 25 } >
268
309
Value
269
310
</ TableHeader > ,
311
+ < TableHeader key = "action" width = { 10 } >
312
+ Action
313
+ </ TableHeader > ,
270
314
< TableHeader key = "masterKeyOnly" width = { 15 } >
271
315
Master key only
272
316
</ TableHeader > ,
@@ -430,6 +474,89 @@ class Config extends TableView {
430
474
modalMasterKeyOnly : false ,
431
475
} ) ;
432
476
}
477
+
478
+ showNote ( message , isError ) {
479
+ if ( ! message ) {
480
+ return ;
481
+ }
482
+ clearTimeout ( this . noteTimeout ) ;
483
+ if ( isError ) {
484
+ this . setState ( { lastError : message , lastNote : null } ) ;
485
+ } else {
486
+ this . setState ( { lastNote : message , lastError : null } ) ;
487
+ }
488
+ this . noteTimeout = setTimeout ( ( ) => {
489
+ this . setState ( { lastError : null , lastNote : null } ) ;
490
+ } , 3500 ) ;
491
+ }
492
+
493
+ openAddEntryDialog ( param ) {
494
+ this . setState ( { showAddEntryDialog : true , addEntryParam : param } ) ;
495
+ }
496
+
497
+ closeAddEntryDialog ( ) {
498
+ this . setState ( { showAddEntryDialog : false , addEntryParam : '' } ) ;
499
+ }
500
+
501
+ async addArrayEntry ( param , value ) {
502
+ try {
503
+ this . setState ( { loading : true } ) ;
504
+ const masterKeyOnlyMap = this . props . config . data . get ( 'masterKeyOnly' ) ;
505
+ const masterKeyOnly = masterKeyOnlyMap ?. get ( param ) || false ;
506
+ await Parse . _request (
507
+ 'PUT' ,
508
+ 'config' ,
509
+ {
510
+ params : {
511
+ [ param ] : { __op : 'AddUnique' , objects : [ Parse . _encode ( value ) ] } ,
512
+ } ,
513
+ masterKeyOnly : { [ param ] : masterKeyOnly } ,
514
+ } ,
515
+ { useMasterKey : true }
516
+ ) ;
517
+ await this . props . config . dispatch ( ActionTypes . FETCH ) ;
518
+
519
+ // Update config history
520
+ const limit = this . context . cloudConfigHistoryLimit ;
521
+ const applicationId = this . context . applicationId ;
522
+ const params = this . props . config . data . get ( 'params' ) ;
523
+ const updatedValue = params . get ( param ) ;
524
+ const configHistory = localStorage . getItem ( `${ applicationId } _configHistory` ) ;
525
+ const newHistoryEntry = {
526
+ time : new Date ( ) ,
527
+ value : updatedValue ,
528
+ } ;
529
+
530
+ if ( ! configHistory ) {
531
+ localStorage . setItem (
532
+ `${ applicationId } _configHistory` ,
533
+ JSON . stringify ( {
534
+ [ param ] : [ newHistoryEntry ] ,
535
+ } )
536
+ ) ;
537
+ } else {
538
+ const oldConfigHistory = JSON . parse ( configHistory ) ;
539
+ const updatedHistory = ! oldConfigHistory [ param ]
540
+ ? [ newHistoryEntry ]
541
+ : [ newHistoryEntry , ...oldConfigHistory [ param ] ] . slice ( 0 , limit || 100 ) ;
542
+
543
+ localStorage . setItem (
544
+ `${ applicationId } _configHistory` ,
545
+ JSON . stringify ( {
546
+ ...oldConfigHistory ,
547
+ [ param ] : updatedHistory ,
548
+ } )
549
+ ) ;
550
+ }
551
+
552
+ this . showNote ( 'Entry added' ) ;
553
+ } catch ( e ) {
554
+ this . showNote ( `Failed to add entry: ${ e . message } ` , true ) ;
555
+ } finally {
556
+ this . setState ( { loading : false } ) ;
557
+ }
558
+ this . closeAddEntryDialog ( ) ;
559
+ }
433
560
}
434
561
435
562
export default Config ;
0 commit comments