/*-
* ============LICENSE_START=======================================================
* ONAP CLAMP
* ================================================================================
* Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============LICENSE_END============================================
* ===================================================================
*
*/
import React, { forwardRef } from 'react';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import styled from 'styled-components';
import TemplateMenuService from '../../../api/TemplateService';
import CsvToJson from '../../../utils/CsvToJson';
import MaterialTable, { MTableToolbar } from "material-table";
import IconButton from '@material-ui/core/IconButton';
import Tooltip from '@material-ui/core/Tooltip';
import AddBox from '@material-ui/icons/AddBox';
import ArrowUpward from '@material-ui/icons/ArrowUpward';
import Check from '@material-ui/icons/Check';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import VerticalAlignTopIcon from '@material-ui/icons/VerticalAlignTop';
import VerticalAlignBottomIcon from '@material-ui/icons/VerticalAlignBottom';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import DeleteOutline from '@material-ui/icons/DeleteOutline';
import Edit from '@material-ui/icons/Edit';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Remove from '@material-ui/icons/Remove';
import Search from '@material-ui/icons/Search';
import ViewColumn from '@material-ui/icons/ViewColumn';
const ModalStyled = styled(Modal)`
@media (min-width: 1200px) {
.modal-xl {
max-width: 96%;
}
}
background-color: transparent;
`
const MTableToolbarStyled = styled(MTableToolbar)`
display: flex;
flex-direction: row;
align-items: center;
`
const ColPullLeftStyled = styled(Col)`
display: flex;
flex-direction: row;
align-items: center;
margin-left: -40px;
`
const cellStyle = { border: '1px solid black' };
const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' };
const rowHeaderStyle = { backgroundColor: '#ddd', fontSize: '15pt', text: 'bold', border: '1px solid black' };
let dictList = [];
let subDictFlag = false;
function SelectSubDictType(props) {
const { onChange } = props;
const selectedValues = (e) => {
let options = e.target.options;
let SelectedDictTypes = '';
for (let dictType = 0, values = options.length; dictType < values; dictType++) {
if (options[dictType].selected) {
SelectedDictTypes = SelectedDictTypes.concat(options[dictType].value);
SelectedDictTypes = SelectedDictTypes.concat('|');
}
}
SelectedDictTypes = SelectedDictTypes.slice(0, -1);
onChange(SelectedDictTypes);
}
// When the subDictFlag is true, we need to disable selection of element "type"
return (
string
number
datetime
map
json
);
}
function SubDict(props) {
const { onChange } = props;
const subDicts = [];
subDicts.push('none');
if (dictList !== undefined && dictList.length > 0) {
let item;
for (item in dictList) {
if (dictList[item].secondLevelDictionary === 1) {
subDicts.push(dictList[item].name);
}
}
}
let optionItems = [];
for (let i = 0; i < subDicts.length; ++i) {
if (i === 0) {
optionItems.push({ subDicts[i] } );
} else {
optionItems.push({ subDicts[i] } );
}
}
function selectedValue(e) {
onChange(e.target.value);
}
// When the subDictFlag is true, we need to disable selection of
// the sub-dictionary flag
return (
{ optionItems }
);
}
export default class ManageDictionaries extends React.Component {
constructor(props, context) {
super(props, context);
this.addDictionaryElementRow = this.addDictionaryElementRow.bind(this);
this.addDictionaryRow = this.addDictionaryRow.bind(this);
this.addReplaceDictionaryRequest = this.addReplaceDictionaryRequest.bind(this);
this.clickHandler = this.clickHandler.bind(this);
this.deleteDictionaryElementRow = this.deleteDictionaryElementRow.bind(this);
this.deleteDictionaryRequest = this.deleteDictionaryRequest.bind(this);
this.deleteDictionaryRow = this.deleteDictionaryRow.bind(this);
this.fileSelectedHandler = this.fileSelectedHandler.bind(this);
this.getDictionaries = this.getDictionaries.bind(this);
this.getDictionaryElements = this.getDictionaryElements.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleDictionaryRowClick = this.handleDictionaryRowClick.bind(this);
this.importCsvData = this.importCsvData.bind(this);
this.updateDictionaryElementRow = this.updateDictionaryElementRow.bind(this);
this.updateDictionaryElementsRequest = this.updateDictionaryElementsRequest.bind(this);
this.updateDictionaryRow = this.updateDictionaryRow.bind(this);
this.readOnly = props.readOnly !== undefined ? props.readOnly : false;
this.state = {
show: true,
currentSelectedDictionary: null,
exportFilename: '',
content: null,
dictionaryElements: [],
tableIcons: {
Add: forwardRef((props, ref) => ),
Delete: forwardRef((props, ref) => ),
DetailPanel: forwardRef((props, ref) => ),
Edit: forwardRef((props, ref) => ),
Check: forwardRef((props, ref) => ),
Clear: forwardRef((props, ref) => ),
Export: forwardRef((props, ref) => ),
Filter: forwardRef((props, ref) => ),
FirstPage: forwardRef((props, ref) => ),
LastPage: forwardRef((props, ref) => ),
NextPage: forwardRef((props, ref) => ),
PreviousPage: forwardRef((props, ref) => ),
ResetSearch: forwardRef((props, ref) => ),
Search: forwardRef((props, ref) => ),
SortArrow: forwardRef((props, ref) => ),
ThirdStateCheck: forwardRef((props, ref) => ),
ViewColumn: forwardRef((props, ref) => )
},
dictColumns: [
{
title: "Dictionary Name", field: "name", editable: 'onAdd',
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Sub Dictionary ?", field: "secondLevelDictionary", lookup: { 0: 'No', 1: 'Yes' },
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Dictionary Type", field: "subDictionaryType", lookup: { string: 'string', number: 'number' },
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Updated By", field: "updatedBy", editable: 'never',
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Last Updated Date", field: "updatedDate", editable: 'never',
cellStyle: cellStyle,
headerStyle: headerStyle
}
],
dictElementColumns: [
{
title: "Element Short Name", field: "shortName", editable: 'onAdd',
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Element Name", field: "name",
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Element Description", field: "description",
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Element Type", field: "type",
editComponent: props => (
),
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Sub-Dictionary", field: "subDictionary",
editComponent: props => (
),
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Updated By", field: "updatedBy", editable: 'never',
cellStyle: cellStyle,
headerStyle: headerStyle
},
{
title: "Updated Date", field: "updatedDate", editable: 'never',
cellStyle: cellStyle,
headerStyle: headerStyle
}
]
}
}
componentDidMount() {
this.getDictionaries();
}
getDictionaries() {
TemplateMenuService.getDictionary().then(arrayOfdictionaries => {
this.setState({ dictionaries: arrayOfdictionaries, currentSelectedDictionary: null })
// global variable setting used functional components in this file
dictList = arrayOfdictionaries;
}).catch(() => {
console.error('Failed to retrieve dictionaries');
this.setState({ dictionaries: [], currentSelectedDictionary: null })
});
}
getDictionaryElements(dictionaryName) {
TemplateMenuService.getDictionaryElements(dictionaryName).then(dictionaryElements => {
this.setState({ dictionaryElements: dictionaryElements.dictionaryElements });
this.setState({ currentSelectDictionary: dictionaryName });
}).catch(() => console.error('Failed to retrieve dictionary elements'))
}
clickHandler(rowData) {
this.getDictionaries();
}
handleClose() {
this.setState({ show: false });
this.props.history.push('/');
}
addReplaceDictionaryRequest(dictionaryEntry) {
TemplateMenuService.insDictionary(dictionaryEntry)
.then(resp => {
this.getDictionaries();
})
.catch(() => console.error('Failed to insert new dictionary elements'));
}
updateDictionaryElementsRequest(dictElements) {
let reqData = { "name": this.state.currentSelectedDictionary, 'dictionaryElements': dictElements };
TemplateMenuService.insDictionaryElements(reqData)
.then(resp => {
this.getDictionaryElements(this.state.currentSelectedDictionary)
})
.catch(() => console.error('Failed to update dictionary elements'));
}
deleteDictionaryRequest(dictionaryName) {
TemplateMenuService.deleteDictionary(dictionaryName)
.then(resp => {
this.getDictionaries();
})
.catch(() => console.error('Failed to delete dictionary'));
}
deleteDictionaryElementRequest(dictionaryName, elemenetShortName) {
TemplateMenuService.deleteDictionaryElements({ 'name': dictionaryName, 'shortName': elemenetShortName })
.then(resp => {
this.getDictionaryElements(dictionaryName);
})
.catch(() => console.error('Failed to delete dictionary elements'));
}
fileSelectedHandler = (event) => {
if (event.target.files[0].type === 'text/csv' || event.target.files[0].type === 'application/vnd.ms-excel') {
if (event.target.files && event.target.files[0]) {
const reader = new FileReader();
reader.onload = (e) => {
let errorMessages = this.importCsvData(reader.result);
if (errorMessages !== '') {
alert(errorMessages);
}
}
reader.readAsText(event.target.files[0]);
}
} else {
alert('Please upload .csv extention files only.');
}
}
importCsvData(rawCsvData) {
const jsonKeyNames = ['shortName', 'name', 'description', 'type', 'subDictionary'];
const userHeaderNames = ['Element Short Name', 'Element Name', 'Element Description', 'Element Type', 'Sub-Dictionary'];
const validTypes = ['string', 'number', 'datetime', 'json', 'map'];
let mandatory;
if (subDictFlag) {
mandatory = [true, true, true, false, false];
} else {
mandatory = [true, true, true, true, false];
}
let result = CsvToJson(rawCsvData, ',', '||||', userHeaderNames, jsonKeyNames, mandatory);
let errorMessages = result.errorMessages;
let jsonObjArray = result.jsonObjArray;
let validTypesErrorMesg = '';
for (let i = 0; i < validTypes.length; ++i) {
if (i === 0) {
validTypesErrorMesg = validTypes[i];
} else {
validTypesErrorMesg += ',' + validTypes[i];
}
}
if (errorMessages !== '') {
return errorMessages;
}
// Perform further checks on data that is now in JSON form
let subDictionaries = [];
// NOTE: dictList is a global variable maintained faithfully
// by the getDictionaries() method outside this import
// functionality.
let item;
for (item in dictList) {
if (dictList[item].secondLevelDictionary === 1) {
subDictionaries.push(dictList[item].name);
}
}
;
// Check for valid Sub-Dictionary and Element Type values
subDictionaries = subDictionaries.toString();
let row = 2;
let dictElem;
for (dictElem of jsonObjArray) {
let itemKey;
for (itemKey in dictElem) {
let value = dictElem[itemKey].trim();
let keyIndex = jsonKeyNames.indexOf(itemKey);
if (itemKey === 'shortName' && /[^a-zA-Z0-9-_.]/.test(value)) {
errorMessages += '\n' + userHeaderNames[keyIndex] +
' at row #' + row +
' can only contain alphanumeric characters and periods, hyphens or underscores';
}
if (itemKey === 'type' && validTypes.indexOf(value) < 0) {
errorMessages += '\nInvalid value of "' + value + '" for "' + userHeaderNames[keyIndex] + '" at row #' + row;
errorMessages += '\nValid types are: ' + validTypesErrorMesg;
}
if (value !== "" && itemKey === 'subDictionary' && subDictionaries.indexOf(value) < 0) {
errorMessages += '\nInvalid Sub-Dictionary value of "' + value + '" at row #' + row;
}
}
++row;
}
if (errorMessages === '') {
// We made it through all the checks. Send it to back end
this.updateDictionaryElementsRequest(jsonObjArray);
}
return errorMessages;
}
addDictionaryRow(newData) {
let validData = true;
return new Promise((resolve, reject) => {
setTimeout(() => {
if (/[^a-zA-Z0-9-_.]/.test(newData.name)) {
validData = false;
alert('Please enter alphanumeric input. Only allowed special characters are:(period, hyphen, underscore)');
reject();
}
for (let i = 0; i < this.state.dictionaries.length; i++) {
if (this.state.dictionaries[i].name === newData.name) {
validData = false;
alert(newData.name + ' dictionary name already exists')
reject();
}
}
if (validData) {
this.addReplaceDictionaryRequest(newData);
}
resolve();
}, 1000);
});
}
updateDictionaryRow(newData, oldData) {
let validData = true;
return new Promise((resolve, reject) => {
setTimeout(() => {
if (/[^a-zA-Z0-9-_.]/.test(newData.name)) {
validData = false;
alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
reject();
}
if (validData) {
this.addReplaceDictionaryRequest(newData);
}
resolve();
}, 1000);
});
}
deleteDictionaryRow(oldData) {
return new Promise((resolve, reject) => {
setTimeout(() => {
this.deleteDictionaryRequest(oldData.name);
resolve();
}, 1000);
});
}
addDictionaryElementRow(newData) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let dictionaryElements = this.state.dictionaryElements;
let errorMessages = '';
for (let i = 0; i < this.state.dictionaryElements.length; i++) {
if (this.state.dictionaryElements[i].shortName === newData.shortName) {
alert('Short Name "' + newData.shortName + '" already exists');
reject("");
}
}
// MaterialTable returns no property at all if the user has not touched a
// new column, so we want to add the property with an emptry string
// for several cases if that is the case to simplify other checks.
if (newData.description === undefined) {
newData.description = "";
}
if (newData.subDictionary === undefined) {
newData.subDictionary = null;
}
if (newData.type === undefined) {
newData.type = "";
}
if (!newData.shortName && /[^a-zA-Z0-9-_.]/.test(newData.shortName)) {
errorMessages += '\nShort Name is limited to alphanumeric characters and also period, hyphen, and underscore';
}
if (!newData.shortName) {
errorMessages += '\nShort Name must be specified';
}
if (!newData.name) {
errorMessages += '\nElement Name must be specified';
}
if (!newData.type && !subDictFlag) {
errorMessages += '\nElement Type must be specified';
}
if (errorMessages === '') {
dictionaryElements.push(newData);
this.updateDictionaryElementsRequest([newData]);
resolve();
} else {
alert(errorMessages);
reject("");
}
}, 1000);
});
}
updateDictionaryElementRow(newData, oldData) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let dictionaryElements = this.state.dictionaryElements;
let validData = true;
if (!newData.type) {
validData = false;
alert('Element Type cannot be null');
reject();
}
if (validData) {
const index = dictionaryElements.indexOf(oldData);
dictionaryElements[index] = newData;
this.updateDictionaryElementsRequest([newData]);
}
resolve();
}, 1000);
});
}
deleteDictionaryElementRow(oldData) {
return new Promise((resolve) => {
setTimeout(() => {
this.deleteDictionaryElementRequest(this.state.currentSelectedDictionary, oldData.shortName);
resolve();
}, 1000);
});
}
handleDictionaryRowClick(event, rowData) {
subDictFlag = rowData.secondLevelDictionary === 1 ? true : false;
this.setState({
currentSelectedDictionary: rowData.name,
exportFilename: rowData.name
})
this.getDictionaryElements(rowData.name);
}
render() {
return (
Manage Dictionaries
{ this.state.currentSelectedDictionary === null ?
: null
}
{ this.state.currentSelectedDictionary !== null ?
(
this.fileUpload.click() }>
{
this.fileUpload = fileUpload;
} }
style={ { visibility: 'hidden', width: '1px' } } onChange={ this.fileSelectedHandler }/>
)
} }
editable={ !this.readOnly ?
{
onRowAdd: this.addDictionaryElementRow,
onRowUpdate: this.updateDictionaryElementRow,
onRowDelete: this.deleteDictionaryElementRow
} : undefined
}
/> : null
}
{ this.state.currentSelectedDictionary !== null ? Go Back to Dictionaries List : "" }
Close
);
}
}