2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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 * ===================================================================
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';
52 const ModalStyled = styled(Modal)`
53 background-color: transparent;
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'};
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('|');
71 SelectedDictTypes = SelectedDictTypes.slice(0,-1);
72 onChange(SelectedDictTypes);
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>
87 function SubDict(props) {
88 const {onChange} = props;
90 subDicts.push('Default');
91 if (dictList != "undefined" && dictList.length > 0) {
92 for(var item in dictList) {
93 if(dictList[item].secondLevelDictionary === 1) {
94 subDicts.push(dictList[item].name);
99 var optionItems = subDicts.map(
100 (item) => <option key={item}>{item}</option>
102 function selectedValue (e) {
103 onChange(e.target.value);
106 <select onChange={selectedValue} >
112 export default class ManageDictionaries extends React.Component {
113 constructor(props, context) {
114 super(props, context);
115 this.handleClose = this.handleClose.bind(this);
116 this.getDictionary = this.getDictionary.bind(this);
117 this.getDictionaryElements = this.getDictionaryElements.bind(this);
118 this.clickHandler = this.clickHandler.bind(this);
119 this.addDictionary = this.addDictionary.bind(this);
120 this.deleteDictionary = this.deleteDictionary.bind(this);
121 this.fileSelectedHandler = this.fileSelectedHandler.bind(this);
136 dictionaryElements: [],
138 Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
139 Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
140 Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
141 Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
142 DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
143 Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
144 Export: forwardRef((props, ref) => <VerticalAlignBottomIcon {...props} ref={ref} />),
145 Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
146 FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
147 LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
148 NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
149 PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref} />),
150 ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
151 Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
152 SortArrow: forwardRef((props, ref) => <ArrowUpward {...props} ref={ref} />),
153 ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
154 ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />)
158 title: "Dictionary Name", field: "name",editable: 'onAdd',
159 cellStyle: cellStyle,
160 headerStyle: headerStyle
163 title: "Sub Dictionary ?", field: "secondLevelDictionary", lookup: {0: 'No', 1: 'Yes'},
164 cellStyle: cellStyle,
165 headerStyle: headerStyle
168 title: "Dictionary Type", field: "subDictionaryType",lookup: {string: 'string', number: 'number'},
169 cellStyle: cellStyle,
170 headerStyle: headerStyle
173 title: "Updated By", field: "updatedBy", editable: 'never',
174 cellStyle: cellStyle,
175 headerStyle: headerStyle
178 title: "Last Updated Date", field: "updatedDate", editable: 'never',
179 cellStyle: cellStyle,
180 headerStyle: headerStyle
183 dictElementColumns: [
185 title: "Element Short Name", field: "shortName",editable: 'onAdd',
186 cellStyle: cellStyle,
187 headerStyle: headerStyle
190 title: "Element Name", field: "name",
191 cellStyle: cellStyle,
192 headerStyle: headerStyle
195 title: "Element Description", field: "description",
196 cellStyle: cellStyle,
197 headerStyle: headerStyle
200 title: "Element Type", field: "type",
201 editComponent: props => (
203 <SelectSubDictType value={props.value} onChange={props.onChange} />
206 cellStyle: cellStyle,
207 headerStyle: headerStyle
210 title: "Sub-Dictionary", field: "subDictionary",
211 editComponent: props => (
213 <SubDict value={props.value} onChange={props.onChange} />
216 cellStyle: cellStyle,
217 headerStyle: headerStyle
220 title: "Updated By", field: "updatedBy", editable: 'never',
221 cellStyle: cellStyle,
222 headerStyle: headerStyle
225 title: "Updated Date", field: "updatedDate", editable: 'never',
226 cellStyle: cellStyle,
227 headerStyle: headerStyle
233 componentWillMount() {
234 this.getDictionary();
238 TemplateMenuService.getDictionary().then(dictionaryNames => {
239 this.setState({ dictionaryNames: dictionaryNames })
243 getDictionaryElements(dictionaryName) {
244 TemplateMenuService.getDictionaryElements(dictionaryName).then(dictionaryElements => {
245 dictList = this.state.dictionaryNames;
246 this.setState({ dictionaryElements: dictionaryElements.dictionaryElements});
250 clickHandler(rowData) {
258 this.setState({ show: false });
259 this.props.history.push('/');
263 var modifiedData = [];
264 if(this.state.newDict !== '') {
265 modifiedData = this.state.newDict;
267 modifiedData = {"name": this.state.dictionaryName, 'dictionaryElements': this.state.newDictItem};
269 if(this.state.newDictItem === '') {
270 TemplateMenuService.insDictionary(modifiedData).then(resp => {
273 TemplateMenuService.insDictionaryElements(modifiedData).then(resp => {
279 var modifiedData = [];
280 if(this.state.delData !== '') {
281 modifiedData = this.state.delData.name;
283 modifiedData = {"name": this.state.dictionaryName, "shortName": this.state.delDictItem.shortName};
285 if(this.state.delDictItem === '') {
286 TemplateMenuService.deleteDictionary(modifiedData).then(resp => {
289 TemplateMenuService.deleteDictionaryElements(modifiedData).then(resp => {
294 fileSelectedHandler = (event) => {
296 var dictionaryElements = [];
297 if (event.target.files[0].type === 'text/csv' ) {
298 if (event.target.files && event.target.files[0]) {
299 let reader = new FileReader();
300 reader.onload = function(e) {
301 var dictElems = reader.result.split('\n');
303 var headers = dictElems[0].split(',');
304 for(var i = 0; i < dictElems.length; i++) {
305 var data = dictElems[i].split(',');
307 for(var j = 0; j < data.length; j++) {
308 obj[headers[j].trim()] = data[j].trim();
312 JSON.stringify(jsonObj);
313 const dictKeys = ['Element Short Name','Element Name','Element Description','Element Type','Sub-Dictionary'];
314 const mandatoryKeys = [ 'Element Short Name', 'Element Name', 'Element Type' ];
315 const validTypes = ['string','number','datetime','json','map'];
318 text.setState({validData: false});
319 } else if (headers.length !== dictKeys.length){
320 text.setState({validImport: false});
322 var subDictionaries = [];
323 for(var item in dictList) {
324 if(dictList[item].secondLevelDictionary === 1) {
325 subDictionaries.push(dictList[item].name);
328 subDictionaries = subDictionaries.toString();
330 for (var dictElem of jsonObj){
332 for (var itemKey in dictElem){
333 var value = dictElem[itemKey].trim();
334 if (dictKeys.indexOf(itemKey) < 0){
335 var errorMessage = 'unknown field name of, ' + itemKey + ', found in CSV header';
336 text.setState({validImport: false});
339 } else if (value === "" && mandatoryKeys.indexOf(itemKey) >= 0){
340 errorMessage = 'value for ' + itemKey + ', at row #, ' + row + ', is empty but required';
341 text.setState({validImport: false});
344 } else if (itemKey === 'Element Type' && validTypes.indexOf(value) < 0 && row > 1) {
345 errorMessage = 'invalid dictElemenType of ' + value + ' at row #' + row;
346 text.setState({validImport: false});
349 } else if (value !== "" && itemKey === 'Sub-Dictionary' && subDictionaries.indexOf(value) < 0 && row > 1) {
350 errorMessage = 'invalid subDictionary of ' + value + ' at row #' + row;
351 text.setState({validImport: false});
357 const headerKeys = ['shortName','name','description','type','subDictionary'];
359 for(i = 1; i < dictElems.length; i++) {
360 data = dictElems[i].split(',');
362 for(j = 0; j < data.length; j++) {
363 obj[headerKeys[j].trim()] = data[j].trim();
365 dictionaryElements.push(obj);
367 text.setState({newDictItem: dictionaryElements, addDict: true});
369 reader.readAsText(event.target.files[0]);
371 this.setState({selectedFile: event.target.files[0]})
373 text.setState({validImport: false});
374 alert('Please upload .csv extention files only.');
381 <ModalStyled size="xl" show={this.state.show} onHide={this.handleClose} backdrop="static" keyboard={false} >
382 <Modal.Header closeButton>
383 <Modal.Title>Manage Dictionaries</Modal.Title>
386 {!this.state.dictNameFlag? <MaterialTable
387 title={"Dictionary List"}
388 data={this.state.dictionaryNames}
389 columns={this.state.dictColumns}
390 icons={this.state.tableIcons}
391 onRowClick={(event, rowData) => {this.getDictionaryElements(rowData.name);this.setState({dictNameFlag: true, exportFilename: rowData.name, dictionaryName: rowData.name})}}
393 headerStyle: rowHeaderStyle,
397 new Promise((resolve, reject) => {
400 const dictionaryNames = this.state.dictionaryNames;
401 var validData = true;
402 if(/[^a-zA-Z0-9-_.]/.test(newData.name)) {
404 alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
406 for (var i = 0; i < this.state.dictionaryNames.length; i++) {
407 if (this.state.dictionaryNames[i].name === newData.name) {
409 alert(newData.name + ' dictionary name already exists')
413 dictionaryNames.push(newData);
414 this.setState({ dictionaryNames }, () => resolve());
415 this.setState({addDict: true, newDict: newData});
421 onRowUpdate: (newData, oldData) =>
422 new Promise((resolve, reject) => {
425 const dictionaryNames = this.state.dictionaryNames;
426 var validData = true;
427 if(/[^a-zA-Z0-9-_.]/.test(newData.name)) {
429 alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
432 const index = dictionaryNames.indexOf(oldData);
433 dictionaryNames[index] = newData;
434 this.setState({ dictionaryNames }, () => resolve());
435 this.setState({addDict: true, newDict: newData});
441 onRowDelete: oldData =>
442 new Promise((resolve, reject) => {
445 let data = this.state.dictionaryNames;
446 const index = data.indexOf(oldData);
447 data.splice(index, 1);
448 this.setState({ data }, () => resolve());
449 this.setState({delDict: true, delData: oldData})
457 {this.state.dictNameFlag? <MaterialTable
458 title={"Dictionary Elements List"}
459 data={this.state.dictionaryElements}
460 columns={this.state.dictElementColumns}
461 icons={this.state.tableIcons}
464 exportFileName: this.state.exportFilename,
465 headerStyle:{backgroundColor:'white', fontSize: '15pt', text: 'bold', border: '1px solid black'}
470 <MTableToolbar {...props} />
472 <Grid item container xs={12} alignItems="flex-end" direction="column" justify="flex-end">
473 <Tooltip title="Import" placement = "bottom">
474 <IconButton aria-label="import" onClick={() => this.fileUpload.click()}>
475 <VerticalAlignTopIcon />
480 <input type="file" ref={(fileUpload) => {this.fileUpload = fileUpload;}} style={{ visibility: 'hidden'}} onChange={this.fileSelectedHandler} />
486 new Promise((resolve, reject) => {
489 const dictionaryElements = this.state.dictionaryElements;
490 var validData = true;
491 for (var i = 0; i < this.state.dictionaryElements.length; i++) {
492 if (this.state.dictionaryElements[i].shortName === newData.shortName) {
494 alert(newData.shortname + 'short name already exists')
497 if(/[^a-zA-Z0-9-_.]/.test(newData.shortName)) {
499 alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
503 alert('Element Type cannot be null');
506 dictionaryElements.push(newData);
507 this.setState({ dictionaryElements }, () => resolve());
508 this.setState({addDict: true, newDictItem: [newData]});
514 onRowUpdate: (newData, oldData) =>
515 new Promise((resolve, reject) => {
518 const dictionaryElements = this.state.dictionaryElements;
519 var validData = true;
522 alert('Element Type cannot be null');
525 const index = dictionaryElements.indexOf(oldData);
526 dictionaryElements[index] = newData;
527 this.setState({ dictionaryElements }, () => resolve());
528 this.setState({addDict: true, newDictItem: [newData]});
534 onRowDelete: oldData =>
535 new Promise((resolve, reject) => {
538 let data = this.state.dictionaryElements;
539 const index = data.indexOf(oldData);
540 data.splice(index, 1);
541 this.setState({ data }, () => resolve());
542 this.setState({delDict: true, delDictItem: oldData})
550 {this.state.dictNameFlag?<button onClick={this.clickHandler} style={{marginTop: '25px'}}>Go Back to Dictionaries List</button>:""}
551 {this.state.addDict && this.addDictionary()}
552 {this.state.delDict && this.deleteDictionary()}
555 <Button variant="secondary" type="null" onClick={this.handleClose}>Close</Button>