blob: 18952376592b8e32101c47e7bb42e4a8a2728b21 [file] [log] [blame]
drveerendra50320952020-03-04 20:30:44 -05001/*-
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
24import React from 'react';
25import Button from 'react-bootstrap/Button';
26import Modal from 'react-bootstrap/Modal';
27import styled from 'styled-components';
28import TemplateMenuService from '../../../api/TemplateService';
29import MaterialTable, {MTableToolbar} from "material-table";
30import IconButton from '@material-ui/core/IconButton';
31import Tooltip from '@material-ui/core/Tooltip';
32import Grid from '@material-ui/core/Grid';
33import { forwardRef } from 'react';
34import AddBox from '@material-ui/icons/AddBox';
35import ArrowUpward from '@material-ui/icons/ArrowUpward';
36import Check from '@material-ui/icons/Check';
37import ChevronLeft from '@material-ui/icons/ChevronLeft';
38import VerticalAlignTopIcon from '@material-ui/icons/VerticalAlignTop';
39import VerticalAlignBottomIcon from '@material-ui/icons/VerticalAlignBottom';
40import ChevronRight from '@material-ui/icons/ChevronRight';
41import Clear from '@material-ui/icons/Clear';
42import DeleteOutline from '@material-ui/icons/DeleteOutline';
43import Edit from '@material-ui/icons/Edit';
44import FilterList from '@material-ui/icons/FilterList';
45import FirstPage from '@material-ui/icons/FirstPage';
46import LastPage from '@material-ui/icons/LastPage';
47import Remove from '@material-ui/icons/Remove';
48import Search from '@material-ui/icons/Search';
49import ViewColumn from '@material-ui/icons/ViewColumn';
50
51
52const ModalStyled = styled(Modal)`
53 background-color: transparent;
54`
55const cellStyle = { border: '1px solid black' };
56const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' };
57const rowHeaderStyle = {backgroundColor:'#ddd', fontSize: '15pt', text: 'bold', border: '1px solid black'};
58var dictList = [];
59
60function 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
87function 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
110export 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 var dictNamesingetDict = this.state.dictionaryNames;
240 }
241
242 getDictionaryElements(dictionaryName) {
243 TemplateMenuService.getDictionaryElements(dictionaryName).then(dictionaryElements => {
244 dictList = this.state.dictionaryNames;
245 this.setState({ dictionaryElements: dictionaryElements.dictionaryElements});
246 });
247 }
248
249 clickHandler(rowData) {
250 this.setState({
251 dictNameFlag: false,
252 addDict: false,
253 });
254 }
255
256 handleClose() {
257 this.setState({ show: false });
258 this.props.history.push('/');
259 }
260
261 addDictionary() {
262 var modifiedData = [];
263 if(this.state.newDict !== '') {
264 modifiedData = this.state.newDict;
265 } else {
266 modifiedData = {"name": this.state.dictionaryName, 'dictionaryElements': this.state.newDictItem};
267 }
268 if(this.state.newDictItem === '') {
269 TemplateMenuService.insDictionary(modifiedData).then(resp => {
270 });
271 } else {
272 TemplateMenuService.insDictionaryElements(modifiedData).then(resp => {
273 });
274 }
275 }
276
277 deleteDictionary() {
278 var modifiedData = [];
279 if(this.state.delData !== '') {
280 modifiedData = this.state.delData.name;
281 } else {
282 modifiedData = {"name": this.state.dictionaryName, "shortName": this.state.delDictItem.shortName};
283 }
284 if(this.state.delDictItem === '') {
285 TemplateMenuService.deleteDictionary(modifiedData).then(resp => {
286 });
287 } else {
288 TemplateMenuService.deleteDictionaryElements(modifiedData).then(resp => {
289 });
290 }
291 }
292
293 fileSelectedHandler = (event) => {
294 const text = this;
295 var dictionaryElements = [];
296 if (event.target.files[0].type === 'text/csv' ) {
297 if (event.target.files && event.target.files[0]) {
298 let reader = new FileReader();
299 reader.onload = function(e) {
300 var dictElems = reader.result.split('\n');
301 var jsonObj = [];
302 var headers = dictElems[0].split(',');
303 for(var i = 0; i < dictElems.length; i++) {
304 var data = dictElems[i].split(',');
305 var obj = {};
306 for(var j = 0; j < data.length; j++) {
307 obj[headers[j].trim()] = data[j].trim();
308 }
309 jsonObj.push(obj);
310 }
311 JSON.stringify(jsonObj);
312 const dictKeys = ['Element Short Name','Element Name','Element Description','Element Type','Sub-Dictionary'];
313 const mandatoryKeys = [ 'Element Short Name', 'Element Name', 'Element Type' ];
314 const validTypes = ['string','number','datetime','json','map'];
315 if (!dictElems){
316
317 text.setState({validData: false});
318 } else if (headers.length !== dictKeys.length){
319 text.setState({validImport: false});
320 } else {
321 var subDictionaries = [];
322 for(var item in dictList) {
323 if(dictList[item].secondLevelDictionary === 1) {
324 subDictionaries.push(dictList[item].name);
325 }
326 };
327 subDictionaries = subDictionaries.toString();
328 var row = 0;
329 for (var dictElem of jsonObj){
330 ++row;
331 for (var itemKey in dictElem){
332 var value = dictElem[itemKey].trim();
333 if (dictKeys.indexOf(itemKey) < 0){
334 var errorMessage = 'unknown field name of, ' + itemKey + ', found in CSV header';
335 text.setState({validImport: false});
336 alert(errorMessage);
337 break;
338 } else if (value === "" && mandatoryKeys.indexOf(itemKey) >= 0){
339 errorMessage = 'value for ' + itemKey + ', at row #, ' + row + ', is empty but required';
340 text.setState({validImport: false});
341 alert(errorMessage);
342 break;
343 } else if (itemKey === 'Element Type' && validTypes.indexOf(value) < 0 && row > 1) {
344 errorMessage = 'invalid dictElemenType of ' + value + ' at row #' + row;
345 text.setState({validImport: false});
346 alert(errorMessage);
347 break;
348 } else if (value !== "" && itemKey === 'Sub-Dictionary' && subDictionaries.indexOf(value) < 0 && row > 1) {
349 errorMessage = 'invalid subDictionary of ' + value + ' at row #' + row;
350 text.setState({validImport: false});
351 alert(errorMessage);
352 }
353 }
354 }
355 }
356 const headerKeys = ['shortName','name','description','type','subDictionary'];
357
358 for(i = 1; i < dictElems.length; i++) {
359 data = dictElems[i].split(',');
360 obj = {};
361 for(j = 0; j < data.length; j++) {
362 obj[headerKeys[j].trim()] = data[j].trim();
363 }
364 dictionaryElements.push(obj);
365 }
366 text.setState({newDictItem: dictionaryElements, addDict: true});
367 }
368 reader.readAsText(event.target.files[0]);
369 }
370 this.setState({selectedFile: event.target.files[0]})
371 } else {
372 text.setState({validImport: false});
373 alert('Please upload .csv extention files only.');
374 }
375
376 }
377
378 render() {
379 return (
380 <ModalStyled size="xl" show={this.state.show} onHide={this.handleClose}>
381 <Modal.Header closeButton>
382 <Modal.Title>Manage Dictionaries</Modal.Title>
383 </Modal.Header>
384 <Modal.Body>
385 {!this.state.dictNameFlag? <MaterialTable
386 title={"Dictionary List"}
387 data={this.state.dictionaryNames}
388 columns={this.state.dictColumns}
389 icons={this.state.tableIcons}
390 onRowClick={(event, rowData) => {this.getDictionaryElements(rowData.name);this.setState({dictNameFlag: true, exportFilename: rowData.name, dictionaryName: rowData.name})}}
391 options={{
392 headerStyle: rowHeaderStyle,
393 }}
394 editable={{
395 onRowAdd: newData =>
396 new Promise((resolve, reject) => {
397 setTimeout(() => {
398 {
399 const dictionaryNames = this.state.dictionaryNames;
400 var validData = true;
401 if(/[^a-zA-Z0-9-_.]/.test(newData.name)) {
402 validData = false;
403 alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
404 }
405 for (var i = 0; i < this.state.dictionaryNames.length; i++) {
406 if (this.state.dictionaryNames[i].name === newData.name) {
407 validData = false;
408 alert(newData.name + ' dictionary name already exists')
409 }
410 }
411 if(validData){
412 dictionaryNames.push(newData);
413 this.setState({ dictionaryNames }, () => resolve());
414 this.setState({addDict: true, newDict: newData});
415 }
416 }
417 resolve();
418 }, 1000);
419 }),
420 onRowUpdate: (newData, oldData) =>
421 new Promise((resolve, reject) => {
422 setTimeout(() => {
423 {
424 const dictionaryNames = this.state.dictionaryNames;
425 var validData = true;
426 if(/[^a-zA-Z0-9-_.]/.test(newData.name)) {
427 validData = false;
428 alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
429 }
430 if(validData){
431 const index = dictionaryNames.indexOf(oldData);
432 dictionaryNames[index] = newData;
433 this.setState({ dictionaryNames }, () => resolve());
434 this.setState({addDict: true, newDict: newData});
435 }
436 }
437 resolve();
438 }, 1000);
439 }),
440 onRowDelete: oldData =>
441 new Promise((resolve, reject) => {
442 setTimeout(() => {
443 {
444 let data = this.state.dictionaryNames;
445 const index = data.indexOf(oldData);
446 data.splice(index, 1);
447 this.setState({ data }, () => resolve());
448 this.setState({delDict: true, delData: oldData})
449 }
450 resolve()
451 }, 1000)
452 })
453 }}
454 />:""
455 }
456 {this.state.dictNameFlag? <MaterialTable
457 title={"Dictionary Elements List"}
458 data={this.state.dictionaryElements}
459 columns={this.state.dictElementColumns}
460 icons={this.state.tableIcons}
461 options={{
462 exportButton: true,
463 exportFileName: this.state.exportFilename,
464 headerStyle:{backgroundColor:'white', fontSize: '15pt', text: 'bold', border: '1px solid black'}
465 }}
466 components={{
467 Toolbar: props => (
468 <div>
469 <MTableToolbar {...props} />
470 <div>
471 <Grid item container xs={12} alignItems="flex-end" direction="column" justify="flex-end">
472 <Tooltip title="Import" placement = "bottom">
473 <IconButton aria-label="import" onClick={() => this.fileUpload.click()}>
474 <VerticalAlignTopIcon />
475 </IconButton>
476 </Tooltip>
477 </Grid>
478 </div>
479 <input type="file" ref={(fileUpload) => {this.fileUpload = fileUpload;}} style={{ visibility: 'hidden'}} onChange={this.fileSelectedHandler} />
480 </div>
481 )
482 }}
483 editable={{
484 onRowAdd: newData =>
485 new Promise((resolve, reject) => {
486 setTimeout(() => {
487 {
488 const dictionaryElements = this.state.dictionaryElements;
489 var validData = true;
490 for (var i = 0; i < this.state.dictionaryElements.length; i++) {
491 if (this.state.dictionaryElements[i].shortName === newData.shortName) {
492 validData = false;
493 alert(newData.shortname + 'short name already exists')
494 }
495 }
496 if(/[^a-zA-Z0-9-_.]/.test(newData.shortName)) {
497 validData = false;
498 alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
499 }
500 if(!newData.type){
501 validData = false;
502 alert('Element Type cannot be null');
503 }
504 if(validData){
505 dictionaryElements.push(newData);
506 this.setState({ dictionaryElements }, () => resolve());
507 this.setState({addDict: true, newDictItem: [newData]});
508 }
509 }
510 resolve();
511 }, 1000);
512 }),
513 onRowUpdate: (newData, oldData) =>
514 new Promise((resolve, reject) => {
515 setTimeout(() => {
516 {
517 const dictionaryElements = this.state.dictionaryElements;
518 var validData = true;
519 if(!newData.type){
520 validData = false;
521 alert('Element Type cannot be null');
522 }
523 if(validData){
524 const index = dictionaryElements.indexOf(oldData);
525 dictionaryElements[index] = newData;
526 this.setState({ dictionaryElements }, () => resolve());
527 this.setState({addDict: true, newDictItem: [newData]});
528 }
529 }
530 resolve();
531 }, 1000);
532 }),
533 onRowDelete: oldData =>
534 new Promise((resolve, reject) => {
535 setTimeout(() => {
536 {
537 let data = this.state.dictionaryElements;
538 const index = data.indexOf(oldData);
539 data.splice(index, 1);
540 this.setState({ data }, () => resolve());
541 this.setState({delDict: true, delDictItem: oldData})
542 }
543 resolve()
544 }, 1000)
545 })
546 }}
547 />:""
548 }
549 {this.state.dictNameFlag?<button onClick={this.clickHandler} style={{marginTop: '25px'}}>Go Back to Dictionaries List</button>:""}
550 {this.state.addDict && this.addDictionary()}
551 {this.state.delDict && this.deleteDictionary()}
552 </Modal.Body>
553 <Modal.Footer>
554 <Button variant="secondary" type="null" onClick={this.handleClose}>Close</Button>
555 </Modal.Footer>
556 </ModalStyled>
557 );
558 }
559}