diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/Beamformer.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/Beamformer.js index 2f88732c6cbf3881720be4e71722f88bee88f0b4..a8df873f12363c26278b137c69f286fec2eb7763 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/Beamformer.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/Beamformer.js @@ -180,7 +180,7 @@ export default class Beamformer extends Component { async copyBeamformersValue(){ this.previousValue = this.state.paramsOutput; await this.props.context.componentParent.updateCell( - this.props.node.rowIndex,this.props.colDef.field, this.state.paramsOutput, true + this.props.node.rowIndex,this.props.colDef.field, this.state.paramsOutput, true, true ); this.setState({ showDialog: false}); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/BetweenEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/BetweenEditor.js index 7bdbfb159276bba2d6a9175e798a7dc4741400db..9421944207b068f397699d982b21d6a0c5ae9490 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/BetweenEditor.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/BetweenEditor.js @@ -91,7 +91,7 @@ export default class BetweenEditor extends Component { } }); await this.props.context.componentParent.updateTime( - this.props.node.rowIndex,this.props.colDef.field, consolidateDates, true + this.props.node.rowIndex,this.props.colDef.field, consolidateDates, true, true ); this.setState({ showDialog: false}); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/CustomDateComp.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/CustomDateComp.js index 9aa21589193f4df54ec183788ec64d42bfde2f26..b17a2f03255cac0797ce600681b7fda83213a7b5 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/CustomDateComp.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/CustomDateComp.js @@ -35,7 +35,7 @@ export default class CustomDateComp extends Component { isCancelAfterEnd(){ let date = (this.state.date !== '' && this.state.date !== undefined)? moment(this.state.date).format(UIConstants.CALENDAR_DATETIME_FORMAT) :''; this.props.context.componentParent.updateTime( - this.props.node.rowIndex,this.props.colDef.field, date + this.props.node.rowIndex,this.props.colDef.field, date, false, true ); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/DegreeInputmask.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/DegreeInputmask.js index 4fd0b705175e3ae8cd5757583545cdb1cb2b552e..9585e69d19e1ba7e19888b541e6a0a7566eda38b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/DegreeInputmask.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/DegreeInputmask.js @@ -8,6 +8,17 @@ export default class DegreeInputMask extends Component { constructor(props) { super(props); this.callbackUpdateAngle = this.callbackUpdateAngle.bind(this); + this.onFocusLost = this.onFocusLost.bind(this); + } + + /** + * Call parent fuction when focus is left from current cell + * @param {*} e + */ + onFocusLost(e) { + this.props.context.componentParent.onFocusLost( + this.props.node.rowIndex,this.props.colDef.field,e.target.value + ); } /** @@ -38,7 +49,7 @@ export default class DegreeInputMask extends Component { title="Enter in dms or degrees or radians" className="inputmask" htmlRef={(ref) => this.input = ref } - onChange={this.callbackUpdateAngle} /> + onChange={this.callbackUpdateAngle} onBlur={this.onFocusLost} /> ); } } \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/OffsetTimeInputmask.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/OffsetTimeInputmask.js index 910dce06b3ac7d3a29912bbcd7416e82bcbc3147..5e19e22abf9017676031beb416a42c4990da8ad8 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/OffsetTimeInputmask.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/OffsetTimeInputmask.js @@ -7,8 +7,15 @@ export default class OffsetTimeInputMask extends Component { constructor(props) { super(props); this.callback = this.callback.bind(this); + this.onFocusLost = this.onFocusLost.bind(this); } + onFocusLost(e) { + this.props.context.componentParent.onFocusLost( + this.props.node.rowIndex,this.props.colDef.field,e.target.value + ); + } + /** * call back function to set value into grid * @param {*} e @@ -38,7 +45,7 @@ export default class OffsetTimeInputMask extends Component { title="Enter in hms format" className="inputmask" htmlRef={(ref) => this.input = ref } - onChange={this.callback} /> + onChange={this.callback} onBlur={this.onFocusLost} /> ); } } \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/StationEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/StationEditor.js index 8abb6b6e457ea9bb06f7ab68125014905c4bd26d..1cad779039dae3d9e651efd42e9f1d67d51b3659 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/StationEditor.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/StationEditor.js @@ -132,7 +132,7 @@ async updateStationGroup() { } await this.props.context.componentParent.updateCell( - this.props.node.rowIndex,this.props.colDef.field, stationValue, true + this.props.node.rowIndex,this.props.colDef.field, stationValue, true, true ); this.setState({ showDialog: false}); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/TimeInputmask.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/TimeInputmask.js index e9fded9bc9803e22aca4f76fe8dd56ecbdacfc35..1376503f28786360ab1d755f8171784b6302e1f2 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/TimeInputmask.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/TimeInputmask.js @@ -8,6 +8,13 @@ export default class TimeInputMask extends Component { constructor(props) { super(props); this.callbackUpdateAngle = this.callbackUpdateAngle.bind(this); + this.onFocusLost = this.onFocusLost.bind(this); + } + + onFocusLost(e) { + this.props.context.componentParent.onFocusLost( + this.props.node.rowIndex,this.props.colDef.field,e.target.value + ); } callbackUpdateAngle(e) { @@ -36,7 +43,7 @@ export default class TimeInputMask extends Component { title="Enter in hms or hours or radians" className="inputmask" htmlRef={(ref) => this.input = ref } - onChange={this.callbackUpdateAngle} /> + onChange={this.callbackUpdateAngle} onBlur={this.onFocusLost} /> ); } } \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/CustomDialog.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/CustomDialog.js index 51e994b80c5f877646918d474bb45be8c67b0d66..91cffa436258075c353647bd486b4c7501f34ad1 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/CustomDialog.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/CustomDialog.js @@ -52,7 +52,7 @@ export class CustomDialog extends Component { {showIcon && <div className="col-lg-2 col-md-2 col-sm-2"> <span style={{position: 'absolute', top: '50%', '-ms-transform': 'translateY(-50%)', transform: 'translateY(-50%)'}}> - <i className={`pi pi-large ${iconClass}`} style={{color: isError?'red':''}}></i> + <i className={`pi pi-large ${iconClass}`}></i> </span> </div> } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_customdialog.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_customdialog.scss index 4ab4801ad46a6073f5fb79b3deac3a0ebd5f8df5..bc11a6148acc6bd9c0da79df39673bf25ee55b03 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_customdialog.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_customdialog.scss @@ -1,4 +1,8 @@ .custom-dlg .p-dialog-content { display: block !important; +} + +.pi-danger { + color: red; } \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js index b7207da04f352d77c556f305d817f0326f100ba5..342086c44438609e49b96847b97e798b20f9b901 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js @@ -153,12 +153,12 @@ export class SchedulingSetCreate extends Component { newSet: null, priorityQueuelist: null, validSpecification: false, - validSpecificationMessage: '', + specificationValidationMessage: '', validConstraints: false, - validConstraintsMessage: '' + constraintsValidationMessage: '' }; - this.validConstraintsMessage = {}; - this.validSpecificationMessage = {}; + this.constraintsValidationMessage = {}; + this.specificationValidationMessage = {}; this.durationList = []; this.stationGroupsName = []; this.custId = 1; @@ -184,7 +184,7 @@ export class SchedulingSetCreate extends Component { this.onClose = this.close; this.onCancel =this.close; this.applyToEmptyRowOnly = false; - this.isStrategyValid = true; + this.isSpecificationValid = true; this.isConstraintValid = true; this.dialogWidth = "40vw"; this.dialogType = "confirmation"; @@ -203,7 +203,10 @@ export class SchedulingSetCreate extends Component { this.emptyAGSU = {}; this.priorityQueuelist = []; this.intervelTrigger = null; - this.warningClipboardData = '' + this.warningClipboardData = ''; + this.constraintVariables = []; + this.strategyVariables = []; + this.onProjectChange = this.onProjectChange.bind(this); this.setSchedulingSetParams = this.setSchedulingSetParams.bind(this); this.onStrategyChange = this.onStrategyChange.bind(this); @@ -240,9 +243,9 @@ export class SchedulingSetCreate extends Component { this.observOptionTemplate = this.observOptionTemplate.bind(this); this.copyWarningContent = this.copyWarningContent.bind(this); this.showConstraintError = this.showConstraintError.bind(this); - this.showStrategyError = this.showStrategyError.bind(this); - this.showConstraintDialogContent = this.showConstraintDialogContent.bind(this); - this.showStrategyDialogContent = this.showStrategyDialogContent.bind(this); + this.showSpecificationError = this.showSpecificationError.bind(this); + this.showConstraintErrorDialog = this.showConstraintErrorDialog.bind(this); + this.showSpecificationErrorDialog = this.showSpecificationErrorDialog.bind(this); this.formRules = { // Form validation rules project: {required: true, message: "Select project to get Scheduling Sets"}, scheduling_set_id: {required: true, message: "Select the Scheduling Set"}, @@ -411,17 +414,41 @@ export class SchedulingSetCreate extends Component { * @param {*} params */ cellValueChageEvent(params) { - if( params.value && !_.isEqual(params.value, params.oldValue)) { - let rowData = this.state.rowData; - let paramRowData = rowData[params.rowIndex]; - this.validateSpecificationsDoc(params.rowIndex, paramRowData); - this.validateSchedulingConstraints(params.rowIndex,paramRowData); + let rowData = this.state.rowData; + let paramRowData = rowData[params.rowIndex]; + if( params.value && !_.isEqual(params.value, params.oldValue)) { paramRowData["isDirty"] = true; this.setState({isDirty: true, rowData: rowData}); publish('edit-dirty', true); } + if (_.includes(this.constraintVariables, params.column['colId'])) { + this.validateSchedulingConstraints(params.rowIndex, paramRowData); + } + if (_.includes(this.strategyVariables, params.column['colId'])) { + this.validateSpecificationsDoc(params.rowIndex,paramRowData); + } } + /** + * Validate the updated cell value when the focus leaves + * @param {*} rowIndex + * @param {*} field + * @param {*} value + */ + onFocusLost (rowIndex, field, value) { + let row = {}; + if (!field.startsWith('gdef_')) { + row = this.state.rowData[rowIndex]; + row[field] = value; + if (_.includes(this.constraintVariables, field)) { + this.validateSchedulingConstraints(rowIndex, row); + } + if (_.includes(this.strategyVariables, field)) { + this.validateSpecificationsDoc(rowIndex, row); + } + } + } + /** * If any changes detected warn before cancel the page */ @@ -1352,6 +1379,7 @@ export class SchedulingSetCreate extends Component { // Create Constraint Column for AG Grid columnDefs = await this.getConstraintColumns(columnDefs); let cellProps = {}; + this.strategyVariables= []; //Observation Schema const schema = this.state.paramsSchema; if(schema.properties && !_.isEmpty(schema.properties)) { @@ -1369,7 +1397,7 @@ export class SchedulingSetCreate extends Component { colProperty ={}; cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; - this.colKeyOrder.push(prop+"~"+property.title); + this.strategyVariables.push(prop+"~"+property.title); this.agSUWithDefaultValue[prop+"~"+property.title] = property.default; cellProps['field'] = prop+"~"+property.title; cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, property.title === 'Filter'?_.lowerCase(property.title):'antenna_set'); @@ -1382,7 +1410,7 @@ export class SchedulingSetCreate extends Component { //colProperty ={}; cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; - this.colKeyOrder.push(prop+"~"+property.title); + this.strategyVariables.push(prop+"~"+property.title); this.agSUWithDefaultValue[prop+"~"+property.title] = UnitConverter.getSecsToHHmmss(property.default); cellProps['field'] = prop+"~"+property.title; cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, _.lowerCase(property.title)); @@ -1394,7 +1422,7 @@ export class SchedulingSetCreate extends Component { let children = []; if (defaultKeys.length > 0 && !property.title.endsWith("Beamformers")) { for(const defaultKey of defaultKeys) { - this.colKeyOrder.push(prop+"~"+defaultKey); + this.strategyVariables.push(prop+"~"+defaultKey); if(defaultKey === 'angle1') { this.agSUWithDefaultValue[prop+"~"+defaultKey] = UnitConverter.getAngleInput( property.default[defaultKey], false); } else if(defaultKey === 'angle2') { @@ -1414,7 +1442,7 @@ export class SchedulingSetCreate extends Component { cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; //cellProps.valueSetter= function(params) {}; - this.colKeyOrder.push(prop+"~"+property.title); + this.strategyVariables.push(prop+"~"+property.title); this.agSUWithDefaultValue[prop+"~"+property.title] = property.default; cellProps['field'] = prop+"~"+property.title; cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, _.lowerCase(property.title)); @@ -1435,7 +1463,7 @@ export class SchedulingSetCreate extends Component { if(property.properties[subProp].type === 'array' && property.properties[subProp].items.enum) { await this.getDemixSourceColumnDef(property.properties[subProp], prop, subProp, colProperty, columnMap, children); } else { - this.colKeyOrder.push(prop+"~"+subProp); + this.strategyVariables.push(prop+"~"+subProp); this.agSUWithDefaultValue[prop+"~"+subProp] = property.default[subProp]; let childCellProps = { headerName : _.startCase(subProp), headerTooltip: _.startCase(subProp), field : prop+"~"+subProp}; childCellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, childCellProps, subProp.toLowerCase()); @@ -1452,7 +1480,7 @@ export class SchedulingSetCreate extends Component { if (property.max_length) { cellProps['max_length'] = property.max_length; } - this.colKeyOrder.push(prop+"~"+property.title); + this.strategyVariables.push(prop+"~"+property.title); this.agSUWithDefaultValue[prop+"~"+property.title] = property.default?property.default:''; cellProps['field'] = prop+"~"+property.title; cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, property.title.toLowerCase()); @@ -1475,7 +1503,7 @@ export class SchedulingSetCreate extends Component { let stationField = stationKey;//_.replace(stationKey, ' ', '_'); this.agSUWithDefaultValue[stationField] = stationValue; columnDefs.push({headerName: stationKey, headerTooltip:stationKey, field: stationField, cellRenderer: 'betweenRenderer', cellEditor: 'station', valueSetter: 'newValueSetter'}); - this.colKeyOrder.push(stationField); + this.strategyVariables.push(stationField); cellProp[stationField] =stationField; columnMap[stationField] = cellProp; } @@ -1489,7 +1517,7 @@ export class SchedulingSetCreate extends Component { columnDefs.push({headerName: 'Stations', field: 'stations', cellRenderer: 'betweenRenderer', cellEditor: 'station', valueSetter: 'newValueSetter'}); */ this.getEmptyRow(); - + this.colKeyOrder = [...this.colKeyOrder, ...this.strategyVariables]; let globalColmunDef =_.cloneDeep(columnDefs); globalColmunDef = await this.createGlobalColumnDefs(globalColmunDef, schema); this.setState({colKeyOrder: this.colKeyOrder, globalColmunDef: globalColmunDef, columnDefs: columnDefs, columnMap: columnMap, agSUWithDefaultValue: this.agSUWithDefaultValue}); @@ -1509,7 +1537,7 @@ export class SchedulingSetCreate extends Component { childCellProps['cellEditor'] = 'multiselector'; childCellProps['field'] = prop+"~"+title; childCellProps.valueSetter = function(params) {} //This line required to set selected value in appropriate cell - this.colKeyOrder.push(prop+"~"+title); + this.strategyVariables.push(prop+"~"+title); this.agSUWithDefaultValue[prop+"~"+title] = property.items.default; colProperty[title] = prop+"~"+title; columnMap[title] = colProperty; @@ -1636,6 +1664,7 @@ export class SchedulingSetCreate extends Component { * @returns */ async getConstraintColumns(columnDefs) { + this.constraintVariables= []; // currently only one constraint schema available and not propvided UI to choose constraints, so assign directly this.constraintSchema = this.constraintTemplates[0]; //this.constraintSchema = await this.resolveSchema(this.constraintSchema); @@ -1659,7 +1688,7 @@ export class SchedulingSetCreate extends Component { // move this variable to class variable //Ag-grid Colums definition // Column order to use clipboard copy - this.colKeyOrder.push('scheduler'); + this.constraintVariables.push('scheduler'); let tmpProps = this.resolvedConstraintSchema.properties.scheduler; tmpProps['multiselect'] = false; this.getColumnDefinition('scheduler', tmpProps); @@ -1693,18 +1722,18 @@ export class SchedulingSetCreate extends Component { { headerName: 'After', headerTooltip: 'After', field:'timeafter', editable: true, cellRenderer: 'betweenRenderer',cellEditor: 'agDateInput', valueSetter: 'newValueSetter'}, { headerName: 'Before', headerTooltip: 'Before', field:'timebefore', editable: true, cellRenderer: 'betweenRenderer',cellEditor: 'agDateInput', valueSetter: 'newValueSetter'}, ],}); - this.colKeyOrder.push('timeat'); - this.colKeyOrder.push('timeafter'); - this.colKeyOrder.push('timebefore'); - this.colKeyOrder.push('between'); - this.colKeyOrder.push('notbetween'); - this.colKeyOrder.push('daily'); + this.constraintVariables.push('timeat'); + this.constraintVariables.push('timeafter'); + this.constraintVariables.push('timebefore'); + this.constraintVariables.push('between'); + this.constraintVariables.push('notbetween'); + this.constraintVariables.push('daily'); columnDefs.push({headerName: 'Between', headerTooltip: 'Between', field: 'between',cellRenderer: 'betweenRenderer',cellEditor: 'betweenEditor',valueSetter: 'newValueSetter'}); columnDefs.push({headerName: 'Not Between', headerTooltip: 'Not Between', field: 'notbetween',cellRenderer: 'betweenRenderer',cellEditor: 'betweenEditor',valueSetter: 'newValueSetter'}); - this.colKeyOrder.push('min_target_elevation'); - this.colKeyOrder.push('min_calibrator_elevation'); - this.colKeyOrder.push('offset_from'); - this.colKeyOrder.push('offset_to'); + this.constraintVariables.push('min_target_elevation'); + this.constraintVariables.push('min_calibrator_elevation'); + this.constraintVariables.push('offset_from'); + this.constraintVariables.push('offset_to'); columnDefs.push({headerName: 'Daily', headerTooltip: 'Daily',field: 'daily',cellEditor: 'multiselector', valueSetter: function(params) {}}, {headerName: 'Sky',headerTooltip: 'Sky', children: [ @@ -1759,9 +1788,9 @@ export class SchedulingSetCreate extends Component { ], }); - this.colKeyOrder.push('md_sun'); - this.colKeyOrder.push('md_moon'); - this.colKeyOrder.push('md_jupiter'); + this.constraintVariables.push('md_sun'); + this.constraintVariables.push('md_moon'); + this.constraintVariables.push('md_jupiter'); columnDefs.push({headerName: 'Min_distance', headerTooltip: 'Min_distance',children: [ {headerName: 'Sun (Degrees)', headerTooltip:'Sun (Degrees)', field: 'md_sun', cellEditor: 'numericEditor',cellStyle: function(params) { if (params.value){ @@ -1803,7 +1832,7 @@ export class SchedulingSetCreate extends Component { } },], }); - + this.colKeyOrder = [...this.colKeyOrder, ...this.constraintVariables]; return columnDefs; } @@ -1833,8 +1862,6 @@ export class SchedulingSetCreate extends Component { row['isValid'] = isValid; row['isDirty'] = true; row['isValidSpecDoc'] = isValid; - await this.validateSpecificationsDoc(rowIndex, row, isValid); - await this.validateSchedulingConstraints(rowIndex, row, isValid); /* row[field+'value'] = UnitConverter.parseAngle(value); */ @@ -1848,7 +1875,7 @@ export class SchedulingSetCreate extends Component { /** * CallBack Function : update time value in master grid */ - async updateTime(rowIndex, field, value, isSetFocus) { + async updateTime(rowIndex, field, value, isSetFocus, doBEValidation) { let row = {}; let tmpRowData = []; if ( field.startsWith('gdef_')) { @@ -1864,8 +1891,6 @@ export class SchedulingSetCreate extends Component { row = this.state.rowData[rowIndex]; row[field] = value; row["isDirty"] = true; - await this.validateSpecificationsDoc(rowIndex, row); - await this.validateSchedulingConstraints(rowIndex, row); tmpRowData = this.state.rowData; tmpRowData[rowIndex] = row; await this.setState({rowData: tmpRowData,isDirty: true}); @@ -1886,6 +1911,15 @@ export class SchedulingSetCreate extends Component { this.state.gridApi.setFocusedCell(rowIndex, focusedCell); } } + + if(doBEValidation) { + if (_.includes(this.constraintVariables, field) ) { + await this.validateSchedulingConstraints(rowIndex, row); + } + if (_.includes(this.strategyVariables, field)) { + await this.validateSpecificationsDoc(rowIndex, row); + } + } } /** @@ -1895,7 +1929,7 @@ export class SchedulingSetCreate extends Component { * @param {*} value * @param {Boolean} isMultiselect - It check if particular cell is Multiselect or not */ - async updateCell(rowIndex, field, value, isMultiselect) { + async updateCell(rowIndex, field, value, isMultiselect, doBEValidation) { let row = {}; let tmpRowData = []; if ( field.startsWith('gdef_')) { @@ -1915,8 +1949,9 @@ export class SchedulingSetCreate extends Component { row = this.state.rowData[rowIndex]; row[field] = value && value.param_0? value.param_0:value; row["isDirty"] = true; - await this.validateSpecificationsDoc(rowIndex, row); - await this.validateSchedulingConstraints(rowIndex, row); + // if (_.includes(field,'Station')) { + // await this.validateSpecificationsDoc(rowIndex, row); + // } tmpRowData = this.state.rowData; tmpRowData[rowIndex] = row; await this.setState({rowData: tmpRowData,isDirty: true}); @@ -1928,6 +1963,15 @@ export class SchedulingSetCreate extends Component { this.state.gridApi.setFocusedCell(rowIndex, focusedCell); } } + + if(doBEValidation) { + if (_.includes(this.constraintVariables, field) ) { + await this.validateSchedulingConstraints(rowIndex, row); + } + if (_.includes(this.strategyVariables, field)) { + await this.validateSpecificationsDoc(rowIndex, row); + } + } } /** @@ -2277,7 +2321,7 @@ export class SchedulingSetCreate extends Component { this.saveSU(); } else if (validCount === 0 && inValidCount === 0) { // leave with no change - this.showIcon = true; + this.showIcon = true; this.dialogMsg = 'No valid Scheduling Unit found !'; this.dialogType = 'warning'; this.onClose = () => {this.setState({confirmDialogVisible: false});}; @@ -2335,8 +2379,12 @@ export class SchedulingSetCreate extends Component { } } - showConstraintDialogContent() { - const keys = Object.keys(this.validConstraintsMessage); + /** + * prepare Constraint Error dialog + * @returns + */ + showConstraintErrorDialog() { + const keys = Object.keys(this.constraintsValidationMessage); return <> <React.Fragment > <br/><tr style={{backgroundColor: 'lightgray', textAlign: 'center'}}><th style={{width: '5em'}}> <span>Row Id </span></th> @@ -2346,15 +2394,19 @@ export class SchedulingSetCreate extends Component { {keys.map((key, index) => ( <React.Fragment key={index+10} > <tr style={{backgroundColor: index % 2 === 0 ? '':'#e6e5e5'}}> <td style={{width: '5em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{(parseInt(key)+1)} </span></td> - <td style={{width: '20em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{this.validConstraintsMessage[key]['suName']} </span></td> - <td style={{width:'45em'}}><span key={'label1-'+ index}>{this.validConstraintsMessage[key]['message']}</span> </td></tr> + <td style={{width: '20em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{this.constraintsValidationMessage[key]['suName']} </span></td> + <td style={{width:'45em'}}><span key={'label1-'+ index}>{this.constraintsValidationMessage[key]['message']}</span> </td></tr> </React.Fragment> ))} </> } - showStrategyDialogContent() { - const keys = Object.keys(this.validSpecificationMessage); + /** + * prepare Specification Error dialog + * @returns + */ + showSpecificationErrorDialog() { + const keys = Object.keys(this.specificationValidationMessage); return <> <React.Fragment > <br/><tr style={{backgroundColor: 'lightgray', textAlign: 'center'}}><th style={{width: '5em'}}> <span>Row Id </span></th> @@ -2364,8 +2416,8 @@ export class SchedulingSetCreate extends Component { {keys.map((key, index) => ( <React.Fragment key={index+10} > <tr style={{backgroundColor: index % 2 === 0 ? '':'#e6e5e5'}}> <td style={{width: '5em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{(parseInt(key)+1)} </span></td> - <td style={{width: '20em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{this.validSpecificationMessage[key]['suName']} </span></td> - <td style={{width:'45em'}}><span key={'label1-'+ index}>{this.validSpecificationMessage[key]['message']}</span> </td></tr> + <td style={{width: '20em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{this.specificationValidationMessage[key]['suName']} </span></td> + <td style={{width:'45em'}}><span key={'label1-'+ index}>{this.specificationValidationMessage[key]['message']}</span> </td></tr> </React.Fragment> ))} </> @@ -2396,12 +2448,7 @@ export class SchedulingSetCreate extends Component { if(res && res.length > 1) { let res = paramsOutput[prefix[0]]; if(prefix[1] === 'angle1' || prefix[1] === 'angle2') { - const result = UnitConverter.parseAngle(suRow[colKey]); - if (result === null) { - suRow[colKey] = 899; - } else { - suRow[colKey] = result; - } + suRow[colKey] = UnitConverter.parseAngle(suRow[colKey]); } if(res) { res[prefix[1]] = suRow[colKey]; @@ -2567,8 +2614,8 @@ export class SchedulingSetCreate extends Component { let processCount = 0; const validRows = _.filter(this.state.rowData, {"isValid": true, "isDirty": true}); this.setState({progressPercent: 0}); - for(const suRow of validRows){ - + for(const validRow of validRows){ + let suRow = _.cloneDeep(validRow); delete suRow['custId']; if (!suRow['isValid']){ continue; @@ -2772,7 +2819,7 @@ export class SchedulingSetCreate extends Component { return constraint; } - async getStarategyValueFromGrid(suRowData) { + async getUpdatedSpecificationDoc(suRowData) { //Prepare output Param await this.prepareObservStrategyFromExcelValue(suRowData); let tmpObservStrategy = null; @@ -2803,25 +2850,25 @@ export class SchedulingSetCreate extends Component { */ async validateSpecificationsDoc(rowIndex, suRowData, isValid) { let validationMessage = ''; - let observStrategy = await this.getStarategyValueFromGrid(_.cloneDeep(suRowData)); + let observStrategy = await this.getUpdatedSpecificationDoc(_.cloneDeep(suRowData)); if (observStrategy) { const promises = []; promises.push(ScheduleService.validateSpecificationsDoc(observStrategy.scheduling_unit_template, observStrategy.template)) const validation_result = await Promise.all(promises); if(validation_result.length > 0 && !validation_result[0]['valid']) { - this.validSpecificationMessage[rowIndex] = { - message: _.replaceAll(validation_result[0]['message'], 'Could not validate the specifications_doc. Error:', ''), + this.specificationValidationMessage[rowIndex] = { + message: validation_result[0]['message'].replace('Could not validate the specifications_doc. Error:', ''), suName: suRowData.suname }; validationMessage = "Task Parameters specification is not valid"; - this.isStrategyValid = false; - this.setState({validSpecification: validation_result[0]['valid'], validSpecificationMessage: validationMessage}); + this.isSpecificationValid = false; + this.setState({validSpecification: validation_result[0]['valid'], specificationValidationMessage: validationMessage}); } else { - delete this.validSpecificationMessage[rowIndex]; - if(Object.keys(this.validSpecificationMessage).length>0) { + delete this.specificationValidationMessage[rowIndex]; + if(Object.keys(this.specificationValidationMessage).length>0) { validationMessage = "Task Parameters specification is not valid"; } - this.setState({validSpecificationMessage: validationMessage}); + this.setState({specificationValidationMessage: validationMessage}); } } } @@ -2829,7 +2876,7 @@ export class SchedulingSetCreate extends Component { /** * Common function validate constraints */ - async validateSchedulingConstraints(rowIndex, suRowData, isValid) { + async validateSchedulingConstraints(rowIndex, suRowData) { let constraint = await this.getConstraintValueFromGrid(_.cloneDeep(suRowData)); if (constraint) { const promises = []; @@ -2837,21 +2884,20 @@ export class SchedulingSetCreate extends Component { promises.push(ScheduleService.validateSpecificationsDoc(this.constraintTemplates[0].url, constraint)) const validation_result = await Promise.all(promises); if(validation_result.length > 0 && !validation_result[0]['valid']) { - this.validConstraintsMessage[rowIndex] = { + this.constraintsValidationMessage[rowIndex] = { message: validation_result[0]['message'].replaceAll('Could not validate the specifications_doc. Error:', ''), suName: suRowData.suname - }; - + }; validationMessage = "Constraints specification is not valid"; this.isConstraintValid = false; - this.setState({validConstraints: validation_result[0]['valid'], validConstraintsMessage: validationMessage}); + this.setState({validConstraints: validation_result[0]['valid'], constraintsValidationMessage: validationMessage}); } else { //validationMessage = validation_result[0]['message']; - delete this.validConstraintsMessage[rowIndex]; - if(Object.keys(this.validConstraintsMessage).length>0) { + delete this.constraintsValidationMessage[rowIndex]; + if(Object.keys(this.constraintsValidationMessage).length>0) { validationMessage = "Constraints specification is not valid"; } - this.setState({validConstraintsMessage: validationMessage}); + this.setState({constraintsValidationMessage: validationMessage}); } } } @@ -3190,29 +3236,49 @@ export class SchedulingSetCreate extends Component { let clipboardData = this.state.clipText?this.state.clipText:null;//await this.readClipBoard(); let selectedRowIndex = 0; var focusedCell = this.state.gridApi.getFocusedCell(); - this.isStrategyValid = true; + this.isSpecificationValid = true; this.isConstraintValid = true; if ((selectedRows && selectedRows.length>0)||focusedCell){ const copiedValues = clipboardData.split('\t'); if (copiedValues.length === 2 && focusedCell && focusedCell.column['colId'] === '0') { if (selectedRows && selectedRows.length>0) { - await selectedRows.map(selectedRow =>{ + await selectedRows.map(async selectedRow =>{ selectedRowIndex = selectedRow.rowIndex; this.tmpRowData[selectedRowIndex]["isDirty"] = true; - this.tmpRowData[selectedRowIndex][copiedValues[0]] = this.getValidCellValue(copiedValues[0], copiedValues[0], + this.tmpRowData[selectedRowIndex][copiedValues[0]] = await this.getValidCellValue(copiedValues[0], copiedValues[0], _.includes(["true","false", "FALSE", "TRUE"], copiedValues[1])? JSON.parse(_.lowerCase(copiedValues[1])): _.lowerCase(copiedValues[1])); + if (_.includes(this.constraintVariables, copiedValues[0]) ) { + await this.validateSchedulingConstraints(selectedRowIndex, this.tmpRowData[selectedRowIndex]); + } + if (_.includes(this.strategyVariables, copiedValues[0])) { + await this.validateSpecificationsDoc(selectedRowIndex, this.tmpRowData[selectedRowIndex]); + } }); } } else if (copiedValues.length === 2 && focusedCell && focusedCell.column['colId'] !== '0') { if (focusedCell.rowIndex >= 0) { this.tmpRowData[focusedCell.rowIndex][focusedCell.column['colId']] = this.getValidCellValue(focusedCell.column['colId'], copiedValues[0], copiedValues[1]); this.tmpRowData[focusedCell.rowIndex][focusedCell.column['isDirty']] = true; + if (selectedRows && selectedRows.length === 0 ) { + if (_.includes(this.constraintVariables, focusedCell.column['colId']) ) { + this.validateSchedulingConstraints(focusedCell.rowIndex, this.tmpRowData[focusedCell.rowIndex]); + } + if (_.includes(this.strategyVariables, focusedCell.column['colId'])) { + this.validateSpecificationsDoc(focusedCell.rowIndex, this.tmpRowData[focusedCell.rowIndex]); + } + } } if (selectedRows && selectedRows.length>0) { await selectedRows.map(selectedRow =>{ selectedRowIndex = selectedRow.rowIndex; this.tmpRowData[selectedRowIndex][focusedCell.column['colId']] = this.getValidCellValue(focusedCell.column['colId'], copiedValues[0], copiedValues[1]); this.tmpRowData[selectedRowIndex][focusedCell.column['isDirty']] = true; + if (_.includes(this.constraintVariables, focusedCell.column['colId']) ) { + this.validateSchedulingConstraints(selectedRowIndex, this.tmpRowData[selectedRowIndex]); + } + if (_.includes(this.strategyVariables, focusedCell.column['colId'])) { + this.validateSpecificationsDoc(selectedRowIndex, this.tmpRowData[selectedRowIndex]); + } }); } } else { @@ -3500,8 +3566,8 @@ export class SchedulingSetCreate extends Component { } }; } - this.validateSchedulingConstraints(tmpRow.custId, tmpRow); - this.validateSpecificationsDoc(tmpRow.custId, tmpRow); + this.validateSchedulingConstraints((tmpRow.custId-1), tmpRow); + this.validateSpecificationsDoc((tmpRow.custId-1), tmpRow); } this.state.gridApi.setRowData(gridRows); this.state.gridColumnApi.applyColumnState({ @@ -3579,23 +3645,29 @@ export class SchedulingSetCreate extends Component { this.setState({observStrategyFilters: observStrategyFilters, observStrategies: observStrategies, observStrategy: {}, rowData: []}) } + /** + * Show Constraint Error dialog in UI + */ showConstraintError() { this.actions = [{id: "cancel_btn", title: "Close", callback: this.close},]; this.onClose = this.close; this.dialogType = "error"; this.dialogHeader = "Constraint Validation"; - this.dialogMsg = "Constraint validation failed, details are listed below, please provide the appropriate column value"; - this.dialogContent = this.showConstraintDialogContent; + this.dialogMsg = "One or more input for the constraint specification is invalid. See the details below and update them with correct values."; + this.dialogContent = this.showConstraintErrorDialog; this.setState({confirmDialogVisible: true}); } - showStrategyError() { + /** + * Show Specification Error dialog in UI + */ + showSpecificationError() { this.actions = [{id: "cancel_btn", title: "Close", callback: this.close},]; this.onClose = this.close; this.dialogType = "error"; this.dialogHeader = "Specification Doc Validation"; - this.dialogMsg = "Specification Doc validation failed, details are listed below, please provide the appropriate column value"; - this.dialogContent = this.showStrategyDialogContent; + this.dialogMsg = "One or more input for the task specification is invalid. See the details below and update them with correct values."; + this.dialogContent = this.showSpecificationErrorDialog; this.setState({confirmDialogVisible: true}); } @@ -3815,16 +3887,16 @@ export class SchedulingSetCreate extends Component { <span style={{color: 'red'}}>* Cannot create new Scheduling Units from obsolete strategy. To edit the existing one, please edit from the details page.</span> </div> } - {!this.state.validConstraints && this.state.validConstraintsMessage.length>0 && + {!this.state.validConstraints && this.state.constraintsValidationMessage.length>0 && <div className="p-grid p-justify-start" style={{marginLeft: '10px'}}> - <label className= "error">{this.state.validConstraintsMessage} </label> + <label className= "error">{this.state.constraintsValidationMessage} </label> <i class="fa fa-exclamation-circle" style={{color: '#eb5454', marginLeft: '5px'}} aria-hidden="true" onClick={this.showConstraintError}></i> </div> } - {!this.state.validSpecification && this.state.validSpecificationMessage.length>0 && + {!this.state.validSpecification && this.state.specificationValidationMessage.length>0 && <div className="p-grid p-justify-start" style={{marginLeft: '10px'}}> - <label className= "error">{this.state.validSpecificationMessage} {Object.values(this.validSpecificationMessage)}</label> - <i class="fa fa-exclamation-circle" style={{color: '#eb5454', marginLeft: '5px'}} aria-hidden="true" onClick={this.showStrategyError}></i> + <label className= "error">{this.state.specificationValidationMessage} {Object.values(this.specificationValidationMessage)}</label> + <i class="fa fa-exclamation-circle" style={{color: '#eb5454', marginLeft: '5px'}} aria-hidden="true" onClick={this.showSpecificationError}></i> </div> } <div className="p-grid p-justify-start"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js index 01741b820b8de213ee37eed6b4bba9e22106800c..6c1678e3c14e78ce7ec2969c113b5aea50a40115 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js @@ -89,7 +89,11 @@ const UnitConverter = { let prefix = _.startsWith(time, '-')?-1:1; time = time.replace("-","").replace("+",""); const strSeconds = _.split(time, ":"); - return prefix * (strSeconds[0] * 3600 + strSeconds[1] * 60 + Number(strSeconds[2])); + if (strSeconds.length === 3){ + return prefix * (strSeconds[0] * 3600 + strSeconds[1] * 60 + Number(strSeconds[2])); + } else if (!isNaN(strSeconds)) { + return parseInt(strSeconds); + } } return 0; }, @@ -123,6 +127,7 @@ const UnitConverter = { object[type] = object[type] * (Math.PI / 180); } } + return object; }, /** * Function to convert Angle 1 & 2 input value for UI.