Fix the legacy Guard UI
[clamp.git] / ui-react / src / components / dialogs / ManageDictionaries / ManageDictionaries.js
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP CLAMP
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END============================================
19  * ===================================================================
20  *
21  */
22
23
24 import React from 'react';
25 import Button from 'react-bootstrap/Button';
26 import Modal from 'react-bootstrap/Modal';
27 import styled from 'styled-components';
28 import TemplateMenuService from '../../../api/TemplateService';
29 import MaterialTable, {MTableToolbar} from "material-table";
30 import IconButton from '@material-ui/core/IconButton';
31 import Tooltip from '@material-ui/core/Tooltip';
32 import Grid from '@material-ui/core/Grid';
33 import { forwardRef }  from 'react';
34 import AddBox from '@material-ui/icons/AddBox';
35 import ArrowUpward from '@material-ui/icons/ArrowUpward';
36 import Check from '@material-ui/icons/Check';
37 import ChevronLeft from '@material-ui/icons/ChevronLeft';
38 import VerticalAlignTopIcon from '@material-ui/icons/VerticalAlignTop';
39 import VerticalAlignBottomIcon from '@material-ui/icons/VerticalAlignBottom';
40 import ChevronRight from '@material-ui/icons/ChevronRight';
41 import Clear from '@material-ui/icons/Clear';
42 import DeleteOutline from '@material-ui/icons/DeleteOutline';
43 import Edit from '@material-ui/icons/Edit';
44 import FilterList from '@material-ui/icons/FilterList';
45 import FirstPage from '@material-ui/icons/FirstPage';
46 import LastPage from '@material-ui/icons/LastPage';
47 import Remove from '@material-ui/icons/Remove';
48 import Search from '@material-ui/icons/Search';
49 import ViewColumn from '@material-ui/icons/ViewColumn';
50
51
52 const ModalStyled = styled(Modal)`
53         background-color: transparent;
54 `
55 const cellStyle = { border: '1px solid black' };
56 const headerStyle = { backgroundColor: '#ddd',  border: '2px solid black'       };
57 const rowHeaderStyle = {backgroundColor:'#ddd',  fontSize: '15pt', text: 'bold', border: '1px solid black'};
58 var dictList = [];
59
60 function SelectSubDictType(props) {
61         const {onChange} = props;
62         const selectedValues = (e) => {
63                 var options = e.target.options;
64                 var SelectedDictTypes = '';
65                 for (var dictType = 0, values = options.length; dictType < values; dictType++) {
66                         if (options[dictType].selected) {
67                                 SelectedDictTypes = SelectedDictTypes.concat(options[dictType].value);
68                                 SelectedDictTypes = SelectedDictTypes.concat('|');
69                         }
70                 }
71                 SelectedDictTypes = SelectedDictTypes.slice(0,-1);
72                 onChange(SelectedDictTypes);
73         }
74         return(
75                 <div>
76                         <select multiple={true}  onChange={selectedValues}>
77                                 <option value="string">string</option>
78                                 <option value="number">number</option>
79                                 <option value="datetime">datetime</option>
80                                 <option value="map">map</option>
81                                 <option value="json">json</option>
82                         </select>
83                 </div>
84         )
85 }
86
87 function SubDict(props) {
88         const {onChange} = props;
89         const subDicts = [];
90         subDicts.push('Default');
91         for(var item in dictList) {
92                 if(dictList[item].secondLevelDictionary === 1) {
93                         subDicts.push(dictList[item].name);
94                 }
95         };
96         subDicts.push('');
97         var optionItems = subDicts.map(
98                 (item) => <option key={item}>{item}</option>
99           );
100         function selectedValue (e) {
101                 onChange(e.target.value);
102         }
103         return(
104                 <select onChange={selectedValue} >
105                         {optionItems}
106                 </select>
107         )
108 }
109
110 export default class ManageDictionaries extends React.Component {
111         constructor(props, context) {
112                 super(props, context);
113                 this.handleClose = this.handleClose.bind(this);
114                 this.getDictionary = this.getDictionary.bind(this);
115                 this.getDictionaryElements = this.getDictionaryElements.bind(this);
116                 this.clickHandler = this.clickHandler.bind(this);
117                 this.addDictionary = this.addDictionary.bind(this);
118                 this.deleteDictionary = this.deleteDictionary.bind(this);
119                 this.fileSelectedHandler = this.fileSelectedHandler.bind(this);
120                 this.state = {
121                         show: true,
122                         selectedFile: '',
123                         dictNameFlag: false,
124                         exportFilename: '',
125                         content: null,
126                         newDict: '',
127                         newDictItem: '',
128                         delDictItem: '',
129                         addDict: false,
130                         delData: '',
131                         delDict: false,
132                         validImport: false,
133                         dictionaryNames: [],
134                         dictionaryElements: [],
135       tableIcons: {
136                 Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
137         Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
138         Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
139         Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
140         DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
141         Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
142         Export: forwardRef((props, ref) => <VerticalAlignBottomIcon {...props} ref={ref} />),
143         Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
144         FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
145         LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
146         NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
147         PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref} />),
148         ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
149         Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
150         SortArrow: forwardRef((props, ref) => <ArrowUpward {...props} ref={ref} />),
151         ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
152         ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />)
153       },
154                         dictColumns: [
155                                 {
156                                         title: "Dictionary Name", field: "name",editable: 'onAdd',
157                                         cellStyle: cellStyle,
158                                         headerStyle: headerStyle
159                                 },
160                                 {
161                                         title: "Sub Dictionary ?", field: "secondLevelDictionary", lookup: {0: 'No', 1: 'Yes'},
162                                         cellStyle: cellStyle,
163                                         headerStyle: headerStyle
164                                 },
165                                 {
166                                         title: "Dictionary Type", field: "subDictionaryType",lookup: {string: 'string', number: 'number'},
167                                         cellStyle: cellStyle,
168                                         headerStyle: headerStyle
169                                 },
170                                 {
171                                         title: "Updated By", field: "updatedBy", editable: 'never',
172                                         cellStyle: cellStyle,
173                                         headerStyle: headerStyle
174                                 },
175                                 {
176                                         title: "Last Updated Date", field: "updatedDate", editable: 'never',
177                                         cellStyle: cellStyle,
178                                         headerStyle: headerStyle
179                                 }
180                         ],
181                         dictElementColumns: [
182                                 {
183                                         title: "Element Short Name", field: "shortName",editable: 'onAdd',
184                                         cellStyle: cellStyle,
185                                         headerStyle: headerStyle
186                                 },
187         {
188                                         title: "Element Name", field: "name",
189                                         cellStyle: cellStyle,
190                                         headerStyle: headerStyle
191                                 },
192                                 {
193                                         title: "Element Description", field: "description",
194                                         cellStyle: cellStyle,
195                                         headerStyle: headerStyle
196                                  },
197                                  {
198                                         title: "Element Type", field: "type",
199                                         editComponent: props => (
200                                                 <div>
201                                                         <SelectSubDictType  value={props.value} onChange={props.onChange} />
202                                                 </div>
203                                         ),
204                                         cellStyle: cellStyle,
205                                         headerStyle: headerStyle
206                                  },
207                                  {  
208                                     title: "Sub-Dictionary", field: "subDictionary",
209                                       editComponent: props => (
210                                                  <div>
211                                                          <SubDict  value={props.value} onChange={props.onChange} />
212                                                  </div>
213                                       ),
214                                     cellStyle: cellStyle,
215                                     headerStyle: headerStyle
216                                  },
217                                 {     
218                                         title: "Updated By", field: "updatedBy", editable: 'never',
219                                         cellStyle: cellStyle,
220                                         headerStyle: headerStyle
221                                 },
222                                 {
223                                         title: "Updated Date", field: "updatedDate", editable: 'never',
224                                         cellStyle: cellStyle,
225                                         headerStyle: headerStyle
226                                 }
227                         ]
228                 }
229         }
230
231         componentWillMount() {
232         this.getDictionary();
233     }
234
235     getDictionary() {
236         TemplateMenuService.getDictionary().then(dictionaryNames => {
237             this.setState({ dictionaryNames: dictionaryNames })
238         });
239     }
240
241     getDictionaryElements(dictionaryName) {
242         TemplateMenuService.getDictionaryElements(dictionaryName).then(dictionaryElements => {
243             dictList = this.state.dictionaryNames;
244             this.setState({ dictionaryElements: dictionaryElements.dictionaryElements});
245         });
246     }
247
248     clickHandler(rowData)   {
249         this.setState({
250             dictNameFlag: false,
251             addDict: false,
252     });
253     }
254
255     handleClose() {
256         this.setState({ show: false });
257         this.props.history.push('/');
258     }
259
260     addDictionary() {
261         var modifiedData = [];
262         if(this.state.newDict !== '') {
263             modifiedData = this.state.newDict;
264         } else {
265             modifiedData = {"name": this.state.dictionaryName, 'dictionaryElements': this.state.newDictItem};
266         }
267         if(this.state.newDictItem === '') {
268             TemplateMenuService.insDictionary(modifiedData).then(resp => {
269             });
270         } else {
271             TemplateMenuService.insDictionaryElements(modifiedData).then(resp => {
272             });
273         }
274     }
275
276     deleteDictionary() {
277         var modifiedData = [];
278         if(this.state.delData !== '') {
279             modifiedData = this.state.delData.name;
280         } else {
281             modifiedData = {"name": this.state.dictionaryName, "shortName": this.state.delDictItem.shortName};
282         }
283         if(this.state.delDictItem === '') {
284             TemplateMenuService.deleteDictionary(modifiedData).then(resp => {
285             });
286         } else {
287             TemplateMenuService.deleteDictionaryElements(modifiedData).then(resp => {
288             });
289         }
290     }
291
292     fileSelectedHandler = (event) => {
293         const text = this;
294         var dictionaryElements = [];
295         if (event.target.files[0].type === 'text/csv' ) {
296             if (event.target.files && event.target.files[0]) {
297                 let reader = new FileReader();
298                 reader.onload = function(e) {
299                     var dictElems = reader.result.split('\n');
300                     var jsonObj = [];
301                     var headers = dictElems[0].split(',');
302                     for(var i = 0; i < dictElems.length; i++) {
303                         var data = dictElems[i].split(',');
304                         var obj = {};
305                         for(var j = 0; j < data.length; j++) {
306                             obj[headers[j].trim()] = data[j].trim();
307                         }
308                         jsonObj.push(obj);
309                     }
310                     JSON.stringify(jsonObj);
311                     const dictKeys = ['Element Short Name','Element Name','Element Description','Element Type','Sub-Dictionary'];
312                     const mandatoryKeys = [ 'Element Short Name', 'Element Name', 'Element Type' ];
313                     const validTypes = ['string','number','datetime','json','map'];
314                     if (!dictElems){
315                         
316                         text.setState({validData: false});
317                     } else if (headers.length !== dictKeys.length){
318                         text.setState({validImport: false});
319                     } else {
320                         var subDictionaries = [];
321                         for(var item in dictList) {
322                             if(dictList[item].secondLevelDictionary === 1) {
323                                 subDictionaries.push(dictList[item].name);
324                             }
325                         };
326                         subDictionaries = subDictionaries.toString();
327                         var row = 0;
328                         for (var dictElem of jsonObj){
329                             ++row;
330                             for (var itemKey in dictElem){
331                                 var value = dictElem[itemKey].trim();
332                                 if (dictKeys.indexOf(itemKey) < 0){
333                                     var errorMessage = 'unknown field name of, ' + itemKey + ', found in CSV header';
334                                     text.setState({validImport: false});
335                                     alert(errorMessage);
336                                     break;
337                                 } else if (value === "" && mandatoryKeys.indexOf(itemKey) >= 0){
338                                     errorMessage = 'value for ' + itemKey + ', at row #, ' + row + ', is empty but required';
339                                     text.setState({validImport: false});
340                                     alert(errorMessage);
341                                     break;
342                                 } else if (itemKey === 'Element Type' && validTypes.indexOf(value) < 0 && row > 1) {
343                                     errorMessage = 'invalid dictElemenType of ' + value + ' at row #' + row;
344                                     text.setState({validImport: false});
345                                     alert(errorMessage);
346                                     break;
347                                 } else if (value !== "" && itemKey === 'Sub-Dictionary' && subDictionaries.indexOf(value) < 0 && row > 1) {
348                                     errorMessage = 'invalid subDictionary of ' + value + ' at row #' + row;
349                                     text.setState({validImport: false});
350                                     alert(errorMessage);
351                                 }
352                             }
353                         }
354                     }
355                     const headerKeys = ['shortName','name','description','type','subDictionary'];
356
357                     for(i = 1; i < dictElems.length; i++) {
358                         data = dictElems[i].split(',');
359                         obj = {};
360                         for(j = 0; j < data.length; j++) {
361                             obj[headerKeys[j].trim()] = data[j].trim();
362                         }
363                         dictionaryElements.push(obj);
364                     }
365                     text.setState({newDictItem: dictionaryElements, addDict: true});
366                 }
367                 reader.readAsText(event.target.files[0]);
368             }
369             this.setState({selectedFile: event.target.files[0]})
370         } else {
371             text.setState({validImport: false});
372             alert('Please upload .csv extention files only.');
373         }
374
375     }
376     
377     render() {
378         return (
379             <ModalStyled size="xl" show={this.state.show} onHide={this.handleClose}>
380                 <Modal.Header closeButton>
381                     <Modal.Title>Manage Dictionaries</Modal.Title>
382                 </Modal.Header>
383                 <Modal.Body>
384                     {!this.state.dictNameFlag? <MaterialTable
385                         title={"Dictionary List"}
386                         data={this.state.dictionaryNames}
387                         columns={this.state.dictColumns}
388                         icons={this.state.tableIcons}
389                         onRowClick={(event, rowData) => {this.getDictionaryElements(rowData.name);this.setState({dictNameFlag: true, exportFilename: rowData.name, dictionaryName: rowData.name})}}
390                         options={{
391                             headerStyle: rowHeaderStyle,
392                         }}
393                         editable={{
394                             onRowAdd: newData =>
395                             new Promise((resolve, reject) => {
396                                 setTimeout(() => {
397                                     {
398                                         const dictionaryNames = this.state.dictionaryNames;
399                                         var validData =  true;
400                                         if(/[^a-zA-Z0-9-_.]/.test(newData.name)) {
401                                             validData = false;
402                                             alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
403                                         }
404                                         for (var i = 0; i < this.state.dictionaryNames.length; i++) {
405                                             if (this.state.dictionaryNames[i].name === newData.name) {
406                                                 validData = false;
407                                                 alert(newData.name + ' dictionary name already exists')
408                                             }
409                                         }
410                                         if(validData){
411                                             dictionaryNames.push(newData);
412                                             this.setState({ dictionaryNames }, () => resolve());
413                                             this.setState({addDict: true, newDict: newData});
414                                         }
415                                     }
416                                     resolve();
417                                 }, 1000);
418                             }),
419                             onRowUpdate: (newData, oldData) =>
420                             new Promise((resolve, reject) => {
421                                 setTimeout(() => {
422                                     {
423                                         const dictionaryNames = this.state.dictionaryNames;
424                                         var validData =  true;
425                                         if(/[^a-zA-Z0-9-_.]/.test(newData.name)) {
426                                             validData = false;
427                                             alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
428                                         }
429                                         if(validData){
430                                             const index = dictionaryNames.indexOf(oldData);
431                                             dictionaryNames[index] = newData;
432                                             this.setState({ dictionaryNames }, () => resolve());
433                                             this.setState({addDict: true, newDict: newData});
434                                         }
435                                     }
436                                     resolve();
437                                 }, 1000);
438                             }),
439                             onRowDelete: oldData =>
440                 new Promise((resolve, reject) => {
441                                 setTimeout(() => {
442                                     {
443                                         let data = this.state.dictionaryNames;
444                     const index = data.indexOf(oldData);
445                     data.splice(index, 1);
446                     this.setState({ data }, () => resolve());
447                                         this.setState({delDict: true, delData: oldData})
448                     }
449                     resolve()
450                 }, 1000)
451                 })
452                         }}
453                         />:""
454                     }
455                     {this.state.dictNameFlag? <MaterialTable
456                         title={"Dictionary Elements List"}
457                         data={this.state.dictionaryElements}
458                         columns={this.state.dictElementColumns}
459                         icons={this.state.tableIcons}
460                         options={{
461                             exportButton: true,
462                             exportFileName: this.state.exportFilename,
463                             headerStyle:{backgroundColor:'white',  fontSize: '15pt', text: 'bold', border: '1px solid black'}
464                         }}
465                         components={{
466                             Toolbar: props => (
467                                 <div>
468                                     <MTableToolbar {...props} />
469                                 <div>
470                                     <Grid item container xs={12} alignItems="flex-end" direction="column" justify="flex-end">
471                                         <Tooltip title="Import" placement = "bottom">
472                                             <IconButton aria-label="import" onClick={() => this.fileUpload.click()}>
473                                                 <VerticalAlignTopIcon />
474                                             </IconButton>
475                                         </Tooltip>
476                                     </Grid>
477                                 </div>
478                                 <input type="file" ref={(fileUpload) => {this.fileUpload = fileUpload;}} style={{ visibility: 'hidden'}} onChange={this.fileSelectedHandler} />
479                                 </div>
480                             )
481                         }}
482                         editable={{
483                             onRowAdd: newData =>
484                             new Promise((resolve, reject) => {
485                                 setTimeout(() => {
486                                     {
487                                         const dictionaryElements = this.state.dictionaryElements;
488                                         var validData =  true;
489                                         for (var i = 0; i < this.state.dictionaryElements.length; i++) {
490                                             if (this.state.dictionaryElements[i].shortName === newData.shortName) {
491                                                 validData = false;
492                                                 alert(newData.shortname + 'short name already exists')
493                                             }
494                                         }
495                                         if(/[^a-zA-Z0-9-_.]/.test(newData.shortName)) {
496                                             validData = false;
497                                             alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
498                                         }
499                                         if(!newData.type){
500                                             validData = false;
501                                             alert('Element Type cannot be null');
502                                         }
503                                         if(validData){
504                                             dictionaryElements.push(newData);
505                                             this.setState({ dictionaryElements }, () => resolve());
506                                             this.setState({addDict: true, newDictItem: [newData]});
507                                         }
508                                     }
509                                     resolve();
510                                 }, 1000);
511                             }),
512                             onRowUpdate: (newData, oldData) =>
513                             new Promise((resolve, reject) => {
514                                 setTimeout(() => {
515                                     {
516                                         const dictionaryElements = this.state.dictionaryElements;
517                                         var validData =  true;
518                                         if(!newData.type){
519                                             validData = false;
520                                             alert('Element Type cannot be null');
521                                         }
522                                         if(validData){
523                                             const index = dictionaryElements.indexOf(oldData);
524                                             dictionaryElements[index] = newData;
525                                             this.setState({ dictionaryElements }, () => resolve());
526                                             this.setState({addDict: true, newDictItem: [newData]});
527                                         }
528                                     }
529                                     resolve();
530                                 }, 1000);
531                             }),
532                             onRowDelete: oldData =>
533                 new Promise((resolve, reject) => {
534                                 setTimeout(() => {
535                                     {
536                                         let data = this.state.dictionaryElements;
537                     const index = data.indexOf(oldData);
538                     data.splice(index, 1);
539                     this.setState({ data }, () => resolve());
540                                         this.setState({delDict: true, delDictItem: oldData})
541                     }
542                     resolve()
543                 }, 1000)
544                 })
545                         }}
546                         />:""
547                     }
548                     {this.state.dictNameFlag?<button onClick={this.clickHandler} style={{marginTop: '25px'}}>Go Back to Dictionaries List</button>:""}
549                     {this.state.addDict && this.addDictionary()}
550                     {this.state.delDict && this.deleteDictionary()}
551                 </Modal.Body>
552                 <Modal.Footer>
553                     <Button variant="secondary" type="null" onClick={this.handleClose}>Close</Button>
554                 </Modal.Footer>
555             </ModalStyled>
556         );
557     }
558 }