@@ -9,6 +9,7 @@ import { ActionTypes } from 'lib/stores/ConfigStore';
99import Button from 'components/Button/Button.react' ;
1010import ConfigDialog from 'dashboard/Data/Config/ConfigDialog.react' ;
1111import DeleteParameterDialog from 'dashboard/Data/Config/DeleteParameterDialog.react' ;
12+ import AddArrayEntryDialog from 'dashboard/Data/Config/AddArrayEntryDialog.react' ;
1213import EmptyState from 'components/EmptyState/EmptyState.react' ;
1314import Icon from 'components/Icon/Icon.react' ;
1415import { isDate } from 'lib/DateUtils' ;
@@ -20,9 +21,11 @@ import TableHeader from 'components/Table/TableHeader.react';
2021import TableView from 'dashboard/TableView.react' ;
2122import Toolbar from 'components/Toolbar/Toolbar.react' ;
2223import browserStyles from 'dashboard/Data/Browser/Browser.scss' ;
24+ import configStyles from 'dashboard/Data/Config/Config.scss' ;
2325import { CurrentApp } from 'context/currentApp' ;
2426import Modal from 'components/Modal/Modal.react' ;
2527import equal from 'fast-deep-equal' ;
28+ import Notification from 'dashboard/Data/Browser/Notification.react' ;
2629
2730@subscribeTo ( 'Config' , 'config' )
2831class Config extends TableView {
@@ -41,7 +44,12 @@ class Config extends TableView {
4144 modalMasterKeyOnly : false ,
4245 loading : false ,
4346 confirmModalOpen : false ,
47+ lastError : null ,
48+ lastNote : null ,
49+ showAddEntryDialog : false ,
50+ addEntryParam : '' ,
4451 } ;
52+ this . noteTimeout = null ;
4553 }
4654
4755 componentWillMount ( ) {
@@ -107,6 +115,15 @@ class Config extends TableView {
107115 onConfirm = { this . deleteParam . bind ( this , this . state . modalParam ) }
108116 />
109117 ) ;
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+ ) ;
110127 }
111128
112129 if ( this . state . confirmModalOpen ) {
@@ -127,12 +144,24 @@ class Config extends TableView {
127144 } }
128145 >
129146 < 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?
131149 </ div >
132150 </ Modal >
133151 ) ;
134152 }
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+ ) ;
136165 }
137166
138167 parseValueForModal ( dataValue ) {
@@ -186,7 +215,6 @@ class Config extends TableView {
186215 * Opens the modal dialog to edit the Config parameter.
187216 */
188217 const openModal = async ( ) => {
189-
190218 // Show dialog
191219 this . setState ( {
192220 loading : true ,
@@ -203,7 +231,8 @@ class Config extends TableView {
203231 // Get latest param values
204232 const fetchedParams = this . props . config . data . get ( 'params' ) ;
205233 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 ;
207236
208237 // Parse fetched data
209238 const { modalValue : fetchedModalValue } = this . parseValueForModal ( fetchedValue ) ;
@@ -219,6 +248,8 @@ class Config extends TableView {
219248 // Define column styles
220249 const columnStyleLarge = { width : '30%' , cursor : 'pointer' } ;
221250 const columnStyleSmall = { width : '15%' , cursor : 'pointer' } ;
251+ const columnStyleValue = { width : '25%' , cursor : 'pointer' } ;
252+ const columnStyleAction = { width : '10%' } ;
222253
223254 const openModalValueColumn = ( ) => {
224255 if ( data . value instanceof Parse . File ) {
@@ -241,13 +272,23 @@ class Config extends TableView {
241272 < td style = { columnStyleSmall } onClick = { openModal } >
242273 { type }
243274 </ td >
244- < td style = { columnStyleLarge } onClick = { openModalValueColumn } >
275+ < td style = { columnStyleValue } onClick = { openModalValueColumn } >
245276 { value }
246277 </ 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 >
247288 < td style = { columnStyleSmall } onClick = { openModal } >
248289 { data . masterKeyOnly . toString ( ) }
249290 </ td >
250- < td style = { { textAlign : 'center' } } >
291+ < td style = { { textAlign : 'center' , width : '5%' } } >
251292 < a onClick = { openDeleteParameterDialog } >
252293 < Icon width = { 16 } height = { 16 } name = "trash-solid" fill = "#ff395e" />
253294 </ a >
@@ -264,9 +305,12 @@ class Config extends TableView {
264305 < TableHeader key = "type" width = { 15 } >
265306 Type
266307 </ TableHeader > ,
267- < TableHeader key = "value" width = { 30 } >
308+ < TableHeader key = "value" width = { 25 } >
268309 Value
269310 </ TableHeader > ,
311+ < TableHeader key = "action" width = { 10 } >
312+ Action
313+ </ TableHeader > ,
270314 < TableHeader key = "masterKeyOnly" width = { 15 } >
271315 Master key only
272316 </ TableHeader > ,
@@ -430,6 +474,89 @@ class Config extends TableView {
430474 modalMasterKeyOnly : false ,
431475 } ) ;
432476 }
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+ }
433560}
434561
435562export default Config ;
0 commit comments