diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js index f23dc25825f2a3c547b00154d1d9011cb2a13f45..f7ab5f16c6e3cf55e63d0a2fffa6f07997559af6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js @@ -13,6 +13,8 @@ import $RefParser from 'json-schema-ref-parser'; import "@fortawesome/fontawesome-free/css/all.css"; import flatpickr from 'flatpickr'; import "flatpickr/dist/flatpickr.css"; +import { Button } from 'primereact/button'; + const JSONEditor = require("@json-editor/json-editor").JSONEditor; function Jeditor(props) { @@ -256,6 +258,8 @@ function Jeditor(props) { message: 'Not a valid input. Mimimum: -90:00:00.0000degrees 0r -1.57079632679489661923, Maximum:90:00:00.0000degrees or 1.57079632679489661923' }); } + } else if (schema.validationType === "transitOffset") { + Validator.validateTransitOffset(schema, value, errors, path); } else if (schema.validationType === "distanceOnSky") { // To add eventlistener to the sky distance field based on the validation type set if(_.indexOf(skyDistanceProps, path) === -1) { @@ -375,7 +379,7 @@ function Jeditor(props) { }) } } - }) + }); } // Add Onchange event for Channels Per Subband field and Time Integration field to update Frequency Resolution and Time Resolution for (const channelPath of channelsPerSubbandProps) { @@ -535,8 +539,10 @@ function Jeditor(props) { * Function to get the schema change for specified properties like subbands, duration, column width, etc * @param {Object} properties */ - function getCustomProperties(properties) { + function getCustomProperties(properties, parentProps) { for (const propertyKey in properties) { + parentProps = parentProps?parentProps:[]; + parentProps[propertyKey] = properties[propertyKey]; const propertyValue = properties[propertyKey]; if ((propertyKey.toLowerCase() === 'subbands' && propertyValue.type === 'array') || propertyKey.toLowerCase() === 'list' && propertyValue.type === 'array') { @@ -618,7 +624,7 @@ function Jeditor(props) { propertyValue.properties['frequency_steps']['validationType'] = "pipelineAverage"; propertyValue.properties['frequency_steps']['format'] = "grid"; propertyValue.properties['frequency_steps']['options'] = { "grid_columns": 3 }; - } else if (propertyKey.toLowerCase() === 'duration') { + } else if (propertyKey.toLowerCase() === 'duration') { let newProperty = { "type": "string", "format": "time", @@ -645,6 +651,20 @@ function Jeditor(props) { } } }; + properties[propertyKey] = newProperty; + // durationProps.push(propertyKey); + } else if (propertyKey.toLowerCase() === 'timedelta') { + let newProperty = { + "type": "string", + "title": propertyKey.toLowerCase(), + "description": `${propertyValue.description?propertyValue.description:''} (+/- Hours:Minutes:Seconds)`, + "options": { + "grid_columns": 3, + "inputAttributes": { + "placeholder": "(+/-) HH:MM:SS" + }, + } + }; properties[propertyKey] = newProperty; // durationProps.push(propertyKey); @@ -673,6 +693,21 @@ function Jeditor(props) { newProperty.options.flatpickr["defaultMinute"] = systemTime.minutes(); } properties[propertyKey] = {...propertyValue, ...newProperty}; + } else if (propertyValue['$ref'] && propertyValue['$ref'].toLowerCase().indexOf('#/definitions/timedelta')>=0) { + // set 'required' field value, it required in fields validation + if (parentProps && parentProps.transit_offset && parentProps.transit_offset.required) { + propertyValue["required"] = _.includes(parentProps.transit_offset.required, propertyKey); + } + propertyValue["type"] = "string"; + //propertyValue["format"] = "time"; + propertyValue["title"] = propertyKey.toLowerCase(); + propertyValue["description"] = `${propertyValue.description?propertyValue.description:''} (+/- Hours:Minutes:Seconds)`; + propertyValue["options"] = {"grid_columns": 3, + "inputAttributes": { + "placeholder": "Transit Offset [+/- Hours:Minutes:Seconds]" + }}; + + properties[propertyKey] = {...propertyValue}; } else if (propertyValue instanceof Object) { // by default previously, all field will have format as Grid, but this will fail for calendar, so added property called skipFormat if (propertyKey !== 'properties' && propertyKey !== 'default' && !propertyValue.skipFormat) { @@ -698,7 +733,7 @@ function Jeditor(props) { pointingProps.push(propertyKey); } - getCustomProperties(propertyValue); + getCustomProperties(propertyValue, parentProps); } } } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/OffsetTimeInputmask.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/OffsetTimeInputmask.js new file mode 100644 index 0000000000000000000000000000000000000000..910dce06b3ac7d3a29912bbcd7416e82bcbc3147 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/OffsetTimeInputmask.js @@ -0,0 +1,44 @@ +import React, { Component } from 'react'; +import Cleave from 'cleave.js/react'; + +const BG_COLOR= '#f878788f'; + +export default class OffsetTimeInputMask extends Component { + constructor(props) { + super(props); + this.callback = this.callback.bind(this); + } + + /** + * call back function to set value into grid + * @param {*} e + */ + callback(e) { + let isValid = false; + if (e.target.value.match('/^[\+|\-]([0-1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/')) { + e.target.style.backgroundColor = ''; + isValid = true; + } else { + e.target.style.backgroundColor = BG_COLOR; + } + e.target.style.border = "none"; + this.props.context.componentParent.updateAngle( + this.props.node.rowIndex,this.props.colDef.field,e.target.value,false,isValid + ); + } + + afterGuiAttached(){ + this.input.focus(); + this.input.select(); + } + + render() { + return ( + <Cleave placeholder="[+/- HH:MM:SS]" value={this.props.value} + title="Enter in hms format" + className="inputmask" + htmlRef={(ref) => this.input = ref } + onChange={this.callback} /> + ); + } +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js index f63111d5eebcf1ef118f6a7952aa9b04fb256727..8c048fab80127edc780d2dec50ae17b08f790c81 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js @@ -317,8 +317,18 @@ export class CalendarTimeline extends Component { <div className="sidebar-header-row">{this.state.viewType===UIConstants.timeline.types.NORMAL? (this.state.dayHeaderVisible?`Day${monthDuration}`:`Week${monthDuration}`) :`Week (${this.state.timelineStartDate.week()}) / Day`}</div> - <div className="sidebar-header-row">{this.state.dayHeaderVisible?`UTC(Hr)`:`UTC(Day)`}</div> - <div className="sidebar-header-row">{this.state.dayHeaderVisible?`LST(Hr)`:`LST(Day)`}</div> + <div className="sidebar-header-row"> + <span style={{fontSize:'10px', fontWeight: 600, backgroundColor: '#c40719', marginRight: '10px'}}> + {this.cursorTime?this.cursorTime.utc.format("DD-MMM-YYYY HH:mm:00"):''} + </span> + {this.state.dayHeaderVisible?`UTC(Hr)`:`UTC(Day)`} + </div> + <div className="sidebar-header-row"> + <span style={{fontSize:'10px', fontWeight: 600, backgroundColor: '#c40719', marginRight: '10px'}}> + {this.cursorTime?this.cursorTime.lst:''} + </span> + {this.state.dayHeaderVisible?`LST(Hr)`:`LST(Day)`} + </div> {/* {this.state.viewType === UIConstants.timeline.types.NORMAL && */} <div className="p-grid legend-row" style={{height:this.props.showSunTimings?'0px':'0px'}}> @@ -693,7 +703,10 @@ export class CalendarTimeline extends Component { /** Custom Render function to pass to the CursorMarker component to display cursor labels on cursor movement */ renderCursor({ styles, date }) { const utc = moment(date).utc(); - this.getLSTof(utc); + // For week view get the row date and get the LST date of the cursor for the row date + let onRowGroup = _.find(this.state.group,['id', this.state.onRow]); + let cursorUTC = onRowGroup?onRowGroup.value.clone().hours(utc.hours()).minutes(utc.minutes()).seconds(utc.seconds()):utc; + this.getLSTof(cursorUTC); const cursorLST = this.state.cursorLST; let cursorTextStyles = {}; cursorTextStyles.backgroundColor = '#c40719' @@ -709,11 +722,13 @@ export class CalendarTimeline extends Component { cursorTextStyles.textAlign = "center"; styles.backgroundColor = '#c40719'; styles.display = "block !important"; + styles.zIndex = '999'; + this.cursorTime = {utc: cursorUTC, lst: cursorLST}; return ( <> - <div style={styles} /> + <div style={styles} /> <div style={cursorTextStyles}> - <div>UTC: { utc.format('DD-MMM-YYYY HH:mm:00')}</div> + <div>UTC: { cursorUTC.format('DD-MMM-YYYY HH:mm:00')}</div> <div>LST: {cursorLST}</div> </div> </> @@ -754,6 +769,7 @@ export class CalendarTimeline extends Component { fontSize: isStationView?"10px":"14px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", textAlign: "center"}; + let itemStatus = null; if (item.type === "SCHEDULE" || item.type === "TASK" || item.type==="STATION_TASK" ) { itemContentStyle = {lineHeight: `${Math.floor(itemContext.dimensions.height/(isStationView?1:3))}px`, maxHeight: itemContext.dimensions.height, @@ -761,6 +777,7 @@ export class CalendarTimeline extends Component { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: isStationView?"nowrap":"inherit", textAlign: "center"}; + itemStatus = item.status; } let itemDivStyle = { background: backgroundColor, color: item.color, @@ -771,11 +788,13 @@ export class CalendarTimeline extends Component { }; if (item.type === "SCHEDULE" || item.type === "TASK" || item.type==="STATION_TASK" ) { itemDivStyle.border = item.scheduleMethod === 'dynamic'?"1.5px dashed":"1.5px solid" + } else if (this.state.viewType === UIConstants.timeline.types.WEEKVIEW && item.type === "SUNTIME") { + itemStatus = "undefined"; } return ( <div {...getItemProps({ - className: `rct-item su-${item.status}`, + className: `rct-item ${itemStatus?'su-'+itemStatus:''}`, style: itemDivStyle, onMouseDown: () => { if (item.type !== "SUNTIME") { @@ -887,6 +906,9 @@ export class CalendarTimeline extends Component { && this.props.itemMouseOverCallback) { this.setState({mouseEvent: true}); this.props.itemMouseOverCallback(evt, item); + } else if (this.state.viewType === UIConstants.timeline.types.WEEKVIEW && item.type === "SUNTIME") { + // For week view set the group id to identify the row date + this.setState({onRow: item.group}); } } @@ -900,6 +922,7 @@ export class CalendarTimeline extends Component { this.setState({mouseEvent: true}); this.props.itemMouseOutCallback(evt); } + this.cursorTime = null; } /** @@ -1622,81 +1645,83 @@ export class CalendarTimeline extends Component { </div> {/* } */} </div> + <div onMouseOut={e => this.cursorTime = null }> <Timeline - groups={this.state.group} - items={this.state.items} - // Use these below properties to stop zoom and move - // defaultTimeStart={this.props.defaultStartTime?this.props.defaultStartTime:this.state.defaultStartTime} - // defaultTimeStart={this.state.defaultStartTime} - // defaultTimeEnd={this.state.defaultEndTime} - visibleTimeStart={this.state.defaultStartTime.valueOf()} - visibleTimeEnd={this.state.defaultEndTime.valueOf()} - resizeDetector={containerResizeDetector} - stackItems={this.props.stackItems || false} - traditionalZoom={this.state.zoomAllowed} - minZoom={this.state.minZoom} - maxZoom={this.state.maxZoom} - lineHeight={this.props.rowHeight || 50} itemHeightRatio={1} - sidebarWidth={this.props.sidebarWidth?this.props.sidebarWidth:this.state.sidebarWidth} - timeSteps={this.state.timeSteps} - onZoom={this.onZoom} - onBoundsChange={this.onBoundsChange} - onTimeChange={this.onTimeChange} - itemRenderer={this.renderItem} - canMove={this.state.canMove} - canResize={this.state.canResize} - canChangeGroup={this.state.canChangeGroup}> - <TimelineHeaders className="sticky"> - <SidebarHeader>{({ getRootProps }) => {return this.renderSidebarHeader({ getRootProps })}}</SidebarHeader> - {this.state.weekHeaderVisible && - <DateHeader unit="Week" labelFormat="w"></DateHeader> } - { this.state.dayHeaderVisible && - <DateHeader unit="hour" intervalRenderer={this.renderDayHeader}></DateHeader> } - <DateHeader unit={this.state.lstDateHeaderUnit} intervalRenderer={this.renderUTCDateHeader} ></DateHeader> - {!this.state.isLSTDateHeaderLoading && - // This method keeps updating the header labels, so that the LST values will be displayed after fetching from server - <DateHeader unit={this.state.lstDateHeaderUnit} - intervalRenderer={({ getIntervalProps, intervalContext, data })=>{return this.renderLSTDateHeader({ getIntervalProps, intervalContext, data })}}> - </DateHeader> - // This method will render once but will not update the values after fetching from server - // <DateHeader unit={this.state.lstDateHeaderUnit} intervalRenderer={this.renderLSTDateHeader}></DateHeader> - } - {/* Suntime Header in normal view with sunrise, sunset and night time */} - {/* {this.props.showSunTimings && this.state.viewType === UIConstants.timeline.types.NORMAL && this.state.sunTimeMap && - <CustomHeader height={30} unit="minute" - children={({ headerContext: { intervals }, getRootProps, getIntervalProps, showPeriod, data})=> { - return this.renderNormalSuntimeHeader({ headerContext: { intervals }, getRootProps, getIntervalProps, showPeriod, data})}}> - </CustomHeader> - } */} - </TimelineHeaders> + groups={this.state.group} + items={this.state.items} + // Use these below properties to stop zoom and move + // defaultTimeStart={this.props.defaultStartTime?this.props.defaultStartTime:this.state.defaultStartTime} + // defaultTimeStart={this.state.defaultStartTime} + // defaultTimeEnd={this.state.defaultEndTime} + visibleTimeStart={this.state.defaultStartTime.valueOf()} + visibleTimeEnd={this.state.defaultEndTime.valueOf()} + resizeDetector={containerResizeDetector} + stackItems={this.props.stackItems || false} + traditionalZoom={this.state.zoomAllowed} + minZoom={this.state.minZoom} + maxZoom={this.state.maxZoom} + lineHeight={this.props.rowHeight || 50} itemHeightRatio={1} + sidebarWidth={this.props.sidebarWidth?this.props.sidebarWidth:this.state.sidebarWidth} + timeSteps={this.state.timeSteps} + onZoom={this.onZoom} + onBoundsChange={this.onBoundsChange} + onTimeChange={this.onTimeChange} + itemRenderer={this.renderItem} + canMove={this.state.canMove} + canResize={this.state.canResize} + canChangeGroup={this.state.canChangeGroup}> + <TimelineHeaders className="sticky"> + <SidebarHeader>{({ getRootProps }) => {return this.renderSidebarHeader({ getRootProps })}}</SidebarHeader> + {this.state.weekHeaderVisible && + <DateHeader unit="Week" labelFormat="w"></DateHeader> } + { this.state.dayHeaderVisible && + <DateHeader unit="hour" intervalRenderer={this.renderDayHeader}></DateHeader> } + <DateHeader unit={this.state.lstDateHeaderUnit} intervalRenderer={this.renderUTCDateHeader} ></DateHeader> + {!this.state.isLSTDateHeaderLoading && + // This method keeps updating the header labels, so that the LST values will be displayed after fetching from server + <DateHeader unit={this.state.lstDateHeaderUnit} + intervalRenderer={({ getIntervalProps, intervalContext, data })=>{return this.renderLSTDateHeader({ getIntervalProps, intervalContext, data })}}> + </DateHeader> + // This method will render once but will not update the values after fetching from server + // <DateHeader unit={this.state.lstDateHeaderUnit} intervalRenderer={this.renderLSTDateHeader}></DateHeader> + } + {/* Suntime Header in normal view with sunrise, sunset and night time */} + {/* {this.props.showSunTimings && this.state.viewType === UIConstants.timeline.types.NORMAL && this.state.sunTimeMap && + <CustomHeader height={30} unit="minute" + children={({ headerContext: { intervals }, getRootProps, getIntervalProps, showPeriod, data})=> { + return this.renderNormalSuntimeHeader({ headerContext: { intervals }, getRootProps, getIntervalProps, showPeriod, data})}}> + </CustomHeader> + } */} + </TimelineHeaders> - <TimelineMarkers> - {/* Current time line marker */} - <CustomMarker date={this.state.currentUTC}> - {({ styles, date }) => { - const customStyles = { - ...styles, - backgroundColor: 'green', - width: '2px' - } - return <div style={customStyles} /> - }} - </CustomMarker> - {/* Show sunrise and sunset markers for normal timeline view (Not station view and week view */} - {this.props.showSunTimings && this.state.viewType===UIConstants.timeline.types.NORMAL && - <> - {/* Sunrise time line markers */} - { this.renderSunriseMarkers(this.state.sunRiseTimings)} - {/* Sunset time line markers */} - { this.renderSunsetMarkers(this.state.sunSetTimings)} - </> - } - {this.state.showCursor? - <CursorMarker> - {this.renderCursor} - </CursorMarker>:""} - </TimelineMarkers> - </Timeline> + <TimelineMarkers> + {/* Current time line marker */} + <CustomMarker date={this.state.currentUTC}> + {({ styles, date }) => { + const customStyles = { + ...styles, + backgroundColor: 'green', + width: '2px' + } + return <div style={customStyles} /> + }} + </CustomMarker> + {/* Show sunrise and sunset markers for normal timeline view (Not station view and week view */} + {this.props.showSunTimings && this.state.viewType===UIConstants.timeline.types.NORMAL && + <> + {/* Sunrise time line markers */} + { this.renderSunriseMarkers(this.state.sunRiseTimings)} + {/* Sunset time line markers */} + { this.renderSunsetMarkers(this.state.sunSetTimings)} + </> + } + {this.state.showCursor? + <CursorMarker> + {this.renderCursor} + </CursorMarker>:""} + </TimelineMarkers> + </Timeline> + </div> </React.Fragment> ); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss index 09a9a311f0dcbcedce451d76c7900425c0d5841f..f4d0510ad84b49e92208d92bfde9989be9320719 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss @@ -390,6 +390,10 @@ h3 + div + p { .affectedTask .pi-search { right: unset !important; } + +.p-checkbox-disabled { + cursor: default !important; +} // - hide search icon in multi select drop down .p-multiselect-filter-icon { display: none; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss index 86ff1ffe4446c998375fac7600afed5a24845d54..9dab46d0ab13c8f01c0bf00e7cad315431e9b289 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss @@ -179,6 +179,12 @@ color: orange; } +.su-undefined { + height: 90% !important; + border-top:1px solid rgb(224, 222, 222) !important; + border-radius: 0px; +} + .su-visible { margin-top: 30px; // margin-left: -59px !important; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js index 904623dc61ed600629496a852d1979c737141323..3082a01a403471ce33121d4ab5580ccf83efe55c 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js @@ -22,6 +22,7 @@ import { Button } from 'primereact/button'; import { Dialog } from 'primereact/dialog'; import { RadioButton } from 'primereact/radiobutton'; import { CustomDialog } from '../../layout/components/CustomDialog'; +import { Checkbox } from 'primereact/checkbox'; /** * Component to view the details of a project @@ -301,7 +302,7 @@ export class ProjectView extends Component { <label className="col-lg-2 col-md-2 col-sm-12">Trigger Priority</label> <span className="col-lg-4 col-md-4 col-sm-12">{this.state.project.trigger_priority}</span> <label className="col-lg-2 col-md-2 col-sm-12">Allows Trigger Submission</label> - <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.can_trigger?'fa fa-check-circle':'fa fa-times-circle'}></i></span> + <Checkbox disabled className="p-checkbox-disabled col-lg-4 col-md-4 col-sm-12" checked={this.state.project.can_trigger?true: false}></Checkbox> </div> <div className="p-grid"> <label className="col-lg-2 col-md-2 col-sm-12">Project Category</label> @@ -323,15 +324,16 @@ export class ProjectView extends Component { </div> <div className="p-grid"> <label className="col-lg-2 col-md-2 col-sm-12">Prevent Automatic Deletion After Ingest</label> - <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.auto_pin?'fa fa-check-circle':'fa fa-times-circle'}></i></span> + <Checkbox disabled className="p-checkbox-disabled col-lg-4 col-md-4 col-sm-12" checked={this.state.project.auto_pin?true: false}></Checkbox> <label className="col-lg-2 col-md-2 col-sm-12">Ingest Automatically</label> - <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.auto_ingest?'fa fa-check-circle':'fa fa-times-circle'}></i></span> + <Checkbox disabled className="p-checkbox-disabled col-lg-4 col-md-4 col-sm-12" checked={this.state.project.auto_ingest?true: false}></Checkbox> </div> <div className="p-grid"> <label className="col-lg-2 col-md-2 col-sm-12">Allow commensal TBB</label> - <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.piggyback_allowed_tbb?'fa fa-check-circle':'fa fa-times-circle'}></i></span> + <Checkbox disabled className="p-checkbox-disabled col-lg-4 col-md-4 col-sm-12" checked={this.state.project.piggyback_allowed_tbb?true: false}></Checkbox> <label className="col-lg-2 col-md-2 col-sm-12">Allow commensal AARTFAAC</label> - <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.piggyback_allowed_aartfaac?'fa fa-check-circle':'fa fa-times-circle'}></i></span> + <Checkbox disabled className="p-checkbox-disabled col-lg-4 col-md-4 col-sm-12" checked={this.state.project.piggyback_allowed_aartfaac?true: false}></Checkbox> + </div> <div className="p-fluid"> <div className="p-field p-grid"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.Constraints.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.Constraints.js index fdb33be78e197fbb0e1108e3ae96c4dfe0011fbc..5347d7ee6b0669f9c883e90015eee27ed673b056 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.Constraints.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.Constraints.js @@ -59,9 +59,11 @@ export default (props) => { } if(propertyKey === 'from' ){ propertyValue.propertyOrder=10; + propertyValue.validationType= 'transitOffset'; } if(propertyKey === 'to'){ propertyValue.propertyOrder=11; + propertyValue.validationType= 'transitOffset'; } if(propertyKey === 'sun' || propertyKey === 'moon' || propertyKey === 'jupiter'){ propertyValue.default = ((propertyValue.default * 180) / Math.PI).toFixed(2); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js index 79f864c821c952b4e40feba4fd35f36906bf320b..a13e0ad5dc71fe1c36582ec497d295de69039da9 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js @@ -31,6 +31,7 @@ import ParserUtility from '../../utils/parser.utility'; import AuthUtil from '../../utils/auth.util'; import AccessDenied from '../../layout/components/AccessDenied'; import AuthStore from '../../authenticate/auth.store'; +import { Checkbox } from 'primereact/checkbox'; class ViewSchedulingUnit extends Component { lsKeySortColumn = 'SortDataViewSchedulingUnit'; @@ -341,8 +342,10 @@ class ViewSchedulingUnit extends Component { ScheduleService.getSchedulingUnitExtended(schedule_type, schedule_id) .then(async (schedulingUnit) => { if (schedulingUnit) { + schedulingUnit = this.formatConstraintDocForUI(schedulingUnit); ScheduleService.getSchedulingConstraintTemplate(schedulingUnit.scheduling_constraints_template_id) - .then((template) => { + .then(async (template) => { + //template = await ConstraintUtility.updateConstraintSchema(template); this.setState({ scheduleunitId: schedule_id, scheduleunit: schedulingUnit, scheduleunitType: schedule_type, @@ -398,7 +401,8 @@ class ViewSchedulingUnit extends Component { && task.tasktype.toLowerCase() === schedule_type && (task.specifications_doc.station_groups || task.specifications_doc.target?.station_groups) }); const isIngestPresent = _.find(tasks, (task) => { return task.template.type_value === 'ingest'}); - await this.getFilterColumns(this.props.match.params.type.toLowerCase()); + await this.getFilterColumns(this.props.match.params.type.toLowerCase()); + this.setState({ scheduleunitId: schedule_id, scheduleunit: schedulingUnit, @@ -422,7 +426,20 @@ class ViewSchedulingUnit extends Component { } }); } - + + /** + * Format constraint field value for UI + * @param {*} scheduling_constraints_doc + * @returns + */ + formatConstraintDocForUI(scheduleunit) { + if (scheduleunit.scheduling_constraints_doc && !scheduleunit.scheduling_constraints_doc.sky.transit_offset.fromoffset) { + scheduleunit.scheduling_constraints_doc.sky.transit_offset.from = (scheduleunit.scheduling_constraints_doc.sky.transit_offset.from<0?'-':'')+UnitConverter.getSecsToHHmmss(scheduleunit.scheduling_constraints_doc.sky.transit_offset.from); + scheduleunit.scheduling_constraints_doc.sky.transit_offset.to = (scheduleunit.scheduling_constraints_doc.sky.transit_offset.to<0?'-':'')+UnitConverter.getSecsToHHmmss(scheduleunit.scheduling_constraints_doc.sky.transit_offset.to); + } + return scheduleunit; + } + /** * To get task parameters from observation strategy to create custom json schema and load with existing value of the task as input * parameters to pass to the JSON editor. @@ -500,6 +517,7 @@ class ViewSchedulingUnit extends Component { tmpOptionalColumns = _.omit(tmpOptionalColumns,columnDefinitionToRemove) await this.setState({tmpDefaulcolumns: [tmpDefaulColumns], tmpOptionalcolumns:[tmpOptionalColumns], tmpColumnOrders: tmpColumnOrders, columnMap: this.columnMap}) } + /** * Get action menus for page header */ @@ -1859,7 +1877,7 @@ class ViewSchedulingUnit extends Component { <label className="col-lg-2 col-md-2 col-sm-12">Tags</label> <Chips className="p-col-4 chips-readonly" disabled value={this.state.scheduleunit.tags}></Chips> <label className="col-lg-2 col-md-2 col-sm-12">Prevent Automatic Deletion</label> - <span className="col-lg-4 col-md-4 col-sm-12"> <i className={this.state.scheduleunit.output_pinned?'fa fa-check-circle':'fa fa-times-circle'}></i> </span> + <Checkbox disabled className="p-checkbox-disabled col-lg-4 col-md-4 col-sm-12" checked={this.state.scheduleunit.output_pinned?true: false}></Checkbox> </div> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js index a048736ad2884324f4a27adea18e0b282d212aea..85f49409c64edb6e561a45c74816782cd75f4eb6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js @@ -162,7 +162,7 @@ export class SchedulingUnitCreate extends Component { * It generates the JSON schema for JSON editor and defult vales for the parameters to be captured * @param {number} strategyId */ - async changeStrategy (strategyId) { + async changeStrategy (strategyId) { const observStrategy = _.find(this.observStrategies, {'id': strategyId}); let station_group = []; const tasks = observStrategy.template.tasks; @@ -255,8 +255,7 @@ export class SchedulingUnitCreate extends Component { if (jsonOutput.scheduler === 'online' || jsonOutput.scheduler === 'dynamic') { err = err.filter(e => e.path !== 'root.time.at'); } - // this.constraintParamsOutput = jsonOutput; - // condition goes here.. + this.constraintParamsOutput = jsonOutput; this.constraintValidEditor = err.length === 0; if ( !this.state.isDirty && this.state.constraintParamsOutput && !_.isEqual(this.state.constraintParamsOutput, jsonOutput) ) { this.setState({ constraintParamsOutput: jsonOutput, constraintValidEditor: err.length === 0, validForm: this.validateForm(), isDirty: true}); @@ -388,7 +387,7 @@ export class SchedulingUnitCreate extends Component { } if (!constStrategy.time.before) { delete constStrategy.time.before; - } + } if (constStrategy.time[type] && constStrategy.time[type].length) { if (typeof constStrategy.time[type] === 'string') { constStrategy.time[type] = `${moment(constStrategy.time[type]).format("YYYY-MM-DDTHH:mm:ss.SSSSS", { trim: false })}Z`; @@ -401,6 +400,15 @@ export class SchedulingUnitCreate extends Component { } } } + if (constStrategy.sky.transit_offset) { + constStrategy.sky.transit_offset.from = UnitConversion.getHHmmssToSecs(constStrategy.sky.transit_offset.from)*(constStrategy.sky.transit_offset.fromoffset === '-'? -1:1); + constStrategy.sky.transit_offset.to = UnitConversion.getHHmmssToSecs(constStrategy.sky.transit_offset.to)*(constStrategy.sky.transit_offset.tooffset === '-'? -1:1); + delete constStrategy.sky.transit_offset.fromoffset; + delete constStrategy.sky.transit_offset.tooffset + delete this.constraintTemplates[0].schema.properties.sky.properties.transit_offset.properties.fromoffset; + delete this.constraintTemplates[0].schema.properties.sky.properties.transit_offset.properties.tooffset; + } + //station const station_groups = []; (this.state.selectedStations || []).forEach(key => { @@ -484,9 +492,11 @@ export class SchedulingUnitCreate extends Component { this.setState({showDialog: false}); } - constraintStrategy(e){ + async constraintStrategy(e){ let schedulingUnit = { ...this.state.schedulingUnit }; schedulingUnit.scheduling_constraints_template_id = e.id; + this.constraintTemplates[0].schema.properties.sky.properties.transit_offset.properties.from.default = UnitConversion.getSecsToHHmmssWithSign(this.constraintTemplates[0].schema.properties.sky.properties.transit_offset.properties.from.default); + this.constraintTemplates[0].schema.properties.sky.properties.transit_offset.properties.to.default = UnitConversion.getSecsToHHmmssWithSign(this.constraintTemplates[0].schema.properties.sky.properties.transit_offset.properties.to.default); this.setState({ constraintSchema: this.constraintTemplates[0], schedulingUnit}); } @@ -588,17 +598,15 @@ export class SchedulingUnitCreate extends Component { setSUSet(suSet) { this.setState({newSet: suSet}); } - + render() { if (this.state.redirect) { return <Redirect to={ {pathname: this.state.redirect} }></Redirect> } const schema = this.state.paramsSchema; const {scheduleunit_draft} = this.state.userrole; - let jeditor = null; if (schema) { - jeditor = React.createElement(Jeditor, {title: "Task Parameters", schema: schema, initValue: this.state.paramsOutput, diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js index 9b46ee48d6b47905525e0a1846d84f8d01d71b64..602144c605297c8601214d4b36a8581c8358304e 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js @@ -219,10 +219,23 @@ export class EditSchedulingUnit extends Component { } else { this.setState({isLoading: false}); } - this.constraintStrategy(this.constraintTemplates[0], this.state.schedulingUnit.scheduling_constraints_doc) + this.constraintStrategy(this.constraintTemplates[0], this.formatConstraintDocForUI(this.state.schedulingUnit.scheduling_constraints_doc)) }); } + /** + * Format constraint field value for UI + * @param {*} scheduling_constraints_doc + * @returns + */ + formatConstraintDocForUI(scheduling_constraints_doc) { + if (scheduling_constraints_doc) { + scheduling_constraints_doc.sky.transit_offset.from = (scheduling_constraints_doc.sky.transit_offset.from<0?'-':'')+UnitConversion.getSecsToHHmmss(scheduling_constraints_doc.sky.transit_offset.from); + scheduling_constraints_doc.sky.transit_offset.to = (scheduling_constraints_doc.sky.transit_offset.to<0?'-':'')+UnitConversion.getSecsToHHmmss(scheduling_constraints_doc.sky.transit_offset.to); + } + return scheduling_constraints_doc; + } + /** * This is the callback method to be passed to the JSON editor. * JEditor will call this function when there is change in the editor. @@ -394,6 +407,7 @@ export class EditSchedulingUnit extends Component { } } } + /* for (let type in constStrategy.sky.transit_offset) { constStrategy.sky.transit_offset[type] = constStrategy.sky.transit_offset[type] * 60; }*/ @@ -412,6 +426,14 @@ export class EditSchedulingUnit extends Component { }); } const schUnit = { ...this.state.schedulingUnit }; + if (constStrategy.sky.transit_offset) { + constStrategy.sky.transit_offset.from = UnitConversion.getHHmmssToSecs(constStrategy.sky.transit_offset.from)*(constStrategy.sky.transit_offset.fromoffset === '-'? -1:1); + constStrategy.sky.transit_offset.to = UnitConversion.getHHmmssToSecs(constStrategy.sky.transit_offset.to)*(constStrategy.sky.transit_offset.tooffset === '-'? -1:1); + delete constStrategy.sky.transit_offset.fromoffset; + delete constStrategy.sky.transit_offset.tooffset + //delete this.constraintTemplates[0].schema.properties.sky.properties.transit_offset.properties.fromoffset; + //delete this.constraintTemplates[0].schema.properties.sky.properties.transit_offset.properties.tooffset; + } schUnit.scheduling_constraints_doc = constStrategy; //station const station_groups = []; @@ -471,8 +493,8 @@ export class EditSchedulingUnit extends Component { this.setState({showDialog: false}); } - constraintStrategy(schema, initValue){ - this.setState({ constraintSchema: schema, initValue: initValue}); + async constraintStrategy(schema, initValue){ + this.setState({ constraintSchema: schema, initValue: initValue}); } onUpdateStations = (state, selectedStations, missingStationFieldsErrors, customSelectedStations) => { @@ -685,7 +707,7 @@ export class EditSchedulingUnit extends Component { <div className="p-grid p-justify-start"> <div className="p-col-1"> <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveSchedulingUnit} - disabled={!this.state.validEditor || !this.state.validForm} data-testid="save-btn" /> + disabled={!this.state.constraintValidEditor || !this.state.validEditor || !this.state.validForm} data-testid="save-btn" /> </div> <div className="p-col-1"> <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> 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 96cf07632f6431641a3e4a1f2ecffe8b4228fe4b..efbfa1031054b013834144aa940e4d205c6f76d1 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 @@ -9,6 +9,7 @@ import { DataTable } from 'primereact/datatable'; import { Column } from 'primereact/column'; import TimeInputmask from '../../components/Spreadsheet/TimeInputmask' +import OffsetTimeInputmask from '../../components/Spreadsheet/OffsetTimeInputmask' import DegreeInputmask from '../../components/Spreadsheet/DegreeInputmask' import NumericEditor from '../../components/Spreadsheet/numericEditor'; import BetweenEditor from '../../components/Spreadsheet/BetweenEditor'; @@ -102,6 +103,7 @@ export class SchedulingSetCreate extends Component { frameworkComponents: { numericEditor: NumericEditor, timeInputMask: TimeInputmask, + offsetTimeInputmask:OffsetTimeInputmask, degreeInputMask: DegreeInputmask, betweenRenderer: BetweenRenderer, betweenEditor: BetweenEditor, @@ -808,8 +810,8 @@ export class SchedulingSetCreate extends Component { observationProps['min_target_elevation'] = constraint.sky.min_target_elevation; observationProps['min_calibrator_elevation'] = constraint.sky.min_calibrator_elevation; if ( constraint.sky.transit_offset ){ - observationProps['offset_from'] = constraint.sky.transit_offset.from?constraint.sky.transit_offset.from:0; - observationProps['offset_to'] = constraint.sky.transit_offset.to?constraint.sky.transit_offset.to:0; + observationProps['offset_from'] = constraint.sky.transit_offset.from?(constraint.sky.transit_offset.from<0?'-':'')+UnitConverter.getSecsToHHmmss(constraint.sky.transit_offset.from):0; + observationProps['offset_to'] = constraint.sky.transit_offset.to?(constraint.sky.transit_offset.to<0?'-':'')+UnitConverter.getSecsToHHmmss(constraint.sky.transit_offset.to):0; observationProps['offset_from_max'] = this.constraintSchema.schema.properties.sky.properties.transit_offset.properties.from.maximum; observationProps['offset_from_min'] = this.constraintSchema.schema.properties.sky.properties.transit_offset.properties.from.minimum; observationProps['offset_to_max'] = this.constraintSchema.schema.properties.sky.properties.transit_offset.properties.to.maximum; @@ -1124,7 +1126,8 @@ export class SchedulingSetCreate extends Component { {headerName: 'Description', field: 'sudesc', cellStyle: function(params) { if (params.data && params.data.suname && (params.data.suname !== '' && (!params.value || params.value === ''))) { return { backgroundColor: BG_COLOR}; - } else { return { backgroundColor: ''};} + } else { + return { backgroundColor: ''};} },}, {headerName: 'Priority Rank', field: 'priority_rank',cellEditor: 'numericEditor', cellStyle: function(params) { let value = params.data.priority_rank? params.data.priority_rank: params.data.gdef_priority_rank ? params.data.gdef_priority_rank : ''; @@ -1132,8 +1135,10 @@ export class SchedulingSetCreate extends Component { const splitValue = _.split((value+''),"."); if (value < 0 || value > 1 || (splitValue.length > 1 && splitValue[1].length > 4)) { return {backgroundColor: BG_COLOR}; - } else { return {backgroundColor: ''};} - } else { return {backgroundColor: ''};} + } else { + return {backgroundColor: ''};} + } else { + return {backgroundColor: ''};} }}, {headerName: 'Priority Queue', field: 'priority_queue',cellEditor: 'agSelectCellEditor', cellEditorParams: {values: this.state.priorityQueuelist}},] @@ -1342,8 +1347,8 @@ export class SchedulingSetCreate extends Component { this.agSUWithDefaultValue['scheduler'] = this.constraintSchema.schema.properties.scheduler.default; this.agSUWithDefaultValue['min_target_elevation'] = ((this.constraintSchema.schema.properties.sky.properties.min_target_elevation.default * 180) / Math.PI).toFixed(2); this.agSUWithDefaultValue['min_calibrator_elevation'] = ((this.constraintSchema.schema.properties.sky.properties.min_calibrator_elevation.default * 180) / Math.PI).toFixed(2); - this.agSUWithDefaultValue['offset_from'] = 0; - this.agSUWithDefaultValue['offset_to'] = 0; + this.agSUWithDefaultValue['offset_from'] = '00:00:00'; + this.agSUWithDefaultValue['offset_to'] = '00:00:00'; this.agSUWithDefaultValue['offset_from_max'] = this.constraintSchema.schema.properties.sky.properties.transit_offset.properties.from.maximum; this.agSUWithDefaultValue['offset_from_min'] = this.constraintSchema.schema.properties.sky.properties.transit_offset.properties.from.minimum; this.agSUWithDefaultValue['offset_to_max'] = this.constraintSchema.schema.properties.sky.properties.transit_offset.properties.to.maximum; @@ -1398,50 +1403,8 @@ export class SchedulingSetCreate extends Component { } } }, }, - {headerName: 'Offset Window From',field: 'offset_from',cellStyle: function(params) { - if (params.value){ - const maxValue = params.data.offset_from_max? params.data.offset_from_max:params.data.gdef_offset_from_max; - const minValue = params.data.offset_from_min? params.data.offset_from_min:params.data.gdef_offset_from_min; - if (params.value === 'undefined' || params.value === ''){ - return { backgroundColor: ''}; - } - if(params.value === "0"){ - return { backgroundColor: ''}; - } - if (!Number(params.value)){ - return { backgroundColor: BG_COLOR}; - } - else if ( Number(params.value) < minValue || Number(params.value) > maxValue) { - return { backgroundColor: BG_COLOR}; - } else{ - return { backgroundColor: ''}; - } - } else { - return { backgroundColor: ''}; - } - }, }, - {headerName: 'Offset Window To',field: 'offset_to', cellStyle: function(params) { - const maxValue = params.data.offset_to_max? params.data.offset_to_max:params.data.gdef_offset_to_max; - const minValue = params.data.offset_to_min? params.data.offset_to_min:params.data.gdef_offset_to_min; - if (params.value){ - if (params.value === 'undefined' || params.value === ''){ - return { backgroundColor: ''}; - } - if(params.value === "0"){ - return { backgroundColor: ''}; - } - if ( !Number(params.value)){ - return { backgroundColor: BG_COLOR}; - } - else if ( Number(params.value) < minValue || Number(params.value) > maxValue) { - return { backgroundColor: BG_COLOR}; - } else{ - return { backgroundColor: ''}; - } - } else { - return { backgroundColor: ''}; - } - }, }, + {headerName: 'Offset Window From',field: 'offset_from',cellRenderer: 'betweenRenderer',cellEditor: 'offsetTimeInputmask',valueSetter: 'newValueSetter',}, + {headerName: 'Offset Window To',field: 'offset_to', cellRenderer: 'betweenRenderer',cellEditor: 'offsetTimeInputmask',valueSetter: 'newValueSetter', }, ], }); this.colKeyOrder.push('md_sun'); @@ -1681,18 +1644,24 @@ export class SchedulingSetCreate extends Component { errorMsg += column.colDef.headerName+", "; } } else if (column.colId === 'offset_from'){ - if ( typeof rowData[column.colId] === 'undefined' || (rowData[column.colId] && isNaN(rowData[column.colId]))){ + const tmpTime = _.split(rowData[column.colId], ":"); + if ( typeof rowData[column.colId] === 'undefined' || + (tmpTime.length !== 3 || isNaN(tmpTime[1]) || tmpTime[1]>59 || isNaN(tmpTime[2]) || tmpTime[2]>59)){ isValidRow = false; errorMsg += column.colDef.headerName+", "; - } else if ( Number(rowData[column.colId]) < rowData['offset_from_min'] || Number(rowData[column.colId]) > rowData['offset_from_max']) { + // column.colDef.cellStyle = { backgroundColor: BG_COLOR}; + // rowNoColumn.colDef.cellStyle = { backgroundColor: BG_COLOR}; + } else if ( UnitConverter.getHHmmssToSecs(rowData[column.colId]) < rowData['offset_from_min'] || UnitConverter.getHHmmssToSecs(rowData[column.colId]) > rowData['offset_from_max']) { isValidRow = false; errorMsg += column.colDef.headerName+", "; } } else if (column.colId === 'offset_to'){ - if ( typeof rowData[column.colId] === 'undefined' || (rowData[column.colId] && isNaN(rowData[column.colId]))){ + const tmpTime = _.split(rowData[column.colId], ":"); + if ( typeof rowData[column.colId] === 'undefined' || + (tmpTime.length !== 3 || tmpTime[1]>59 || tmpTime[2]>59)){ isValidRow = false; errorMsg += column.colDef.headerName+", "; - } else if ( Number(rowData[column.colId]) < rowData['offset_to_min'] || Number(rowData[column.colId]) > rowData['offset_to_max']) { + } else if ( UnitConverter.getHHmmssToSecs(rowData[column.colId]) < rowData['offset_to_min'] || UnitConverter.getHHmmssToSecs(rowData[column.colId]) > rowData['offset_to_max']) { isValidRow = false; errorMsg += column.colDef.headerName+", "; } @@ -1952,8 +1921,8 @@ export class SchedulingSetCreate extends Component { constraint.sky.min_distance = min_distance_res; let transit_offset_res = {}; - transit_offset_res['from'] = +suRow.offset_from; - transit_offset_res['to'] = +suRow.offset_to; + transit_offset_res['from'] = UnitConverter.getHHmmssToSecs(suRow.offset_from); + transit_offset_res['to'] = UnitConverter.getHHmmssToSecs(suRow.offset_to); if (transit_offset_res){ constraint.sky.transit_offset= transit_offset_res; } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/summary.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/summary.js index af5de42019e732c35b3f7cbc6e9293aaa177deb4..087340e36268ebbdf5fc96479227a153ab48bb3b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/summary.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/summary.js @@ -11,7 +11,7 @@ import { Button } from "primereact/button"; import AuthStore from '../../authenticate/auth.store'; import AuthUtil from '../../utils/auth.util'; import { CustomDialog } from '../../layout/components/CustomDialog'; -import { findAllByDisplayValue } from '@testing-library/dom'; +import UnitConverter from '../../utils/unit.converter'; /** * Component to view summary of the scheduling unit with limited task details @@ -27,6 +27,7 @@ export class SchedulingUnitSummary extends Component { editCheck:1, close:false, userrole: AuthStore.getState(), + constraintsTemplate : this.props.constraintsTemplate, }; this.constraintsOrder = ['scheduler', 'time', 'daily', 'sky']; this.closeSUDets = this.closeSUDets.bind(this); @@ -164,15 +165,49 @@ export class SchedulingUnitSummary extends Component { async componentDidMount() { const promises = [ AuthUtil.getUserRolePermission() - ]; + ]; + this.updateConstraintTemplate(); await Promise.all(promises).then(responses => { this.setState({userrole: responses[0],editCheck:1}); }); } + /** + * Add new fields for plus/minu value for offset (from & to) + * @param {*} constraintTemplate + * @returns + */ + async updateConstraintTemplate() { + if (this.props.constraintsTemplate) { + let constraintTemplate = this.props.constraintsTemplate; + constraintTemplate.schema.properties.sky.properties.transit_offset.properties.from.default = + UnitConverter.getSecsToHHmmssWithSign(this.props.constraintsTemplate.schema.properties.sky.properties.transit_offset.properties.from.default); + constraintTemplate.schema.properties.sky.properties.transit_offset.properties.to.default = + UnitConverter.getSecsToHHmmssWithSign(this.props.constraintsTemplate.schema.properties.sky.properties.transit_offset.properties.to.default); + this.setState({constraintTemplate: constraintTemplate}); + } + } + + /** + * Format constraint field value for UI + * @param {*} scheduling_constraints_doc + * @returns + */ + formatConstraintDocForUI(scheduleunit) { + if (scheduleunit.scheduling_constraints_doc) { + scheduleunit.scheduling_constraints_doc.sky.transit_offset.from = UnitConverter.getSecsToHHmmssWithSign(scheduleunit.scheduling_constraints_doc.sky.transit_offset.from); + scheduleunit.scheduling_constraints_doc.sky.transit_offset.to = UnitConverter.getSecsToHHmmssWithSign(scheduleunit.scheduling_constraints_doc.sky.transit_offset.to); + } + if (this.props.schedulingUnit) { + this.props.schedulingUnit.scheduling_constraints_doc.sky.transit_offset.from = UnitConverter.getSecsToHHmmssWithSign(this.props.schedulingUnit.scheduling_constraints_doc.sky.transit_offset.from); + this.props.schedulingUnit.scheduling_constraints_doc.sky.transit_offset.to = UnitConverter.getSecsToHHmmssWithSign(this.props.schedulingUnit.scheduling_constraints_doc.sky.transit_offset.to); + } + return scheduleunit; + } + render() { const permissions = this.state.userrole.userRolePermission.scheduleunit; - const schedulingUnit = this.props.schedulingUnit; + let schedulingUnit = _.cloneDeep(this.props.schedulingUnit); const suTaskList = this.props.suTaskList; suTaskList.map(task => { task.typeValue = task.specifications_template.type_value; @@ -180,6 +215,7 @@ export class SchedulingUnitSummary extends Component { }); const constraintsTemplate = this.props.constraintsTemplate; // After receiving output from the SchedulingConstraint editor order and format it to display + schedulingUnit = this.formatConstraintDocForUI(schedulingUnit); let constraintsDoc = schedulingUnit.scheduling_constraints_doc ? this.getOrderedConstraints(schedulingUnit.scheduling_constraints_doc,this.constraintsOrder) : null; let disableBtn= this.state.showerror!==0 ?true: false; return ( @@ -305,8 +341,8 @@ export class SchedulingUnitSummary extends Component { } > <SchedulingConstraints - constraintTemplate={constraintsTemplate} - initValue={schedulingUnit.scheduling_constraints_doc} + constraintTemplate={this.state.constraintsTemplate} + initValue={this.props.schedulingUnit.scheduling_constraints_doc} callback={this.setConstraintsEditorOutput} /> </Dialog> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js index db0997e9693b03850e3ba47b7bd6fe8606a94bf0..95ffc42ae4ba635efb3cb198bfa85fe5dd99a0a4 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js @@ -30,6 +30,7 @@ class TimelineListTabs extends Component { this.suListFilterCallback = this.suListFilterCallback.bind(this); this.taskListFilterCallback = this.taskListFilterCallback.bind(this); this.reservListFilterCallback = this.reservListFilterCallback.bind(this); + this.unschedulableCallback = this.unschedulableCallback.bind(this); this.getTaskList = this.getTaskList.bind(this); this.getSUFilterOptions = this.getSUFilterOptions.bind(this); this.getTaskFilterOptions = this.getTaskFilterOptions.bind(this); @@ -136,7 +137,16 @@ class TimelineListTabs extends Component { this.filteredReservs = filteredData; this.props.suListFilterCallback(this.filteredSUB, this.filteredTasks, filteredData); } - + + /** + * Callback function to pass the filtered data for all table data to the parent component to display in the timeline even if the + * selected tab is unscedulable. + * @param {Array} filteredData - Array of unschedulable SUB rows + */ + unschedulableCallback(filteredData) { + this.props.suListFilterCallback(this.filteredSUB, this.filteredTasks, this.filteredReservs); + } + /** * Child Component to display the status log column value with icon and show the dialog on clicking it. * @param {Object} task @@ -300,7 +310,7 @@ class TimelineListTabs extends Component { showTopTotal={false} showGlobalFilter={true} showColumnFilter={true} - // filterCallback={this.suListFilterCallback} + filterCallback={this.unschedulableCallback} lsKeySortColumn={"UnschedulableListSortColumn"} toggleBySorting={(sortData) => this.storeSortingColumn(`${this.props.viewName}_UnschedulableListSortColumn`, sortData)} pageUpdated={this.pageUpdated} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js index 48983d293ba97d78b49b5a1208e537e818eafcc9..d419a076858a7e554f5b3185ed27ebd250775d61 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js @@ -1342,7 +1342,7 @@ export class WeekTimelineView extends Component { itemClickCallback={this.onItemClick} itemMouseOverCallback={this.onItemMouseOver} itemMouseOutCallback={this.onItemMouseOut} - sidebarWidth={150} + sidebarWidth={175} stackItems={true} startTime={moment.utc(this.state.currentUTC).hour(0).minutes(0).seconds(0)} endTime={moment.utc(this.state.currentUTC).hour(23).minutes(59).seconds(59)} 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 dc72950df943ac70367d8f5d5a3763f194dbc79f..22c07f6e8d3e8cf000ebf14ded24ea8951608792 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js @@ -61,19 +61,41 @@ const UnitConverter = { let stopTime = moment(stopdate).unix(); return UnitConverter.getSecsToDDHHmmss(stopTime - startTime); }, + getSecsToHHmmssWithSign: function (seconds) { + var prefix = ''; + if (!isNaN(seconds)) { + if (seconds<0) { + prefix = '-'; + } + seconds = prefix+this.getSecsToHHmmss(seconds); + } + return seconds; + }, getSecsToHHmmss: function (seconds) { - if (seconds >= 0) { + if (!isNaN(seconds)) { + seconds = Math.abs(seconds); const hh = Math.floor(seconds / 3600); const mm = Math.floor((seconds - hh * 3600) / 60); const ss = +((seconds - (hh * 3600) - (mm * 60)) / 1); - return (hh < 10 ? `0${hh}` : `${hh}`) + ':' + (mm < 10 ? `0${mm}` : `${mm}`) + ':' + (ss < 10 ? `0${ss}` : `${ss}`); + let retStr = (hh < 10 ? `0${hh}` : `${hh}`) + ':' + (mm < 10 ? `0${mm}` : `${mm}`) + ':' + (ss < 10 ? `0${ss}` : `${ss}`); + return retStr; } return seconds; }, getHHmmssToSecs: function (seconds) { if (seconds) { + seconds = _.trim(seconds); const strSeconds = _.split(seconds, ":"); - return strSeconds[0] * 3600 + strSeconds[1] * 60 + Number(strSeconds[2]); + let prefix = 1; + if (_.startsWith(strSeconds[0], '-')) { + prefix = -1; + const startPosition = strSeconds[0].indexOf("-"); + strSeconds[0] = strSeconds[0].substring(startPosition+1, strSeconds[0].length); + } else if (_.startsWith(strSeconds[0], '+')) { + const startPosition = strSeconds[0].indexOf("+"); + strSeconds[0] = strSeconds[0].substring(startPosition+1, strSeconds[0].length); + } + return strSeconds.length > 2 ?prefix * (_.trim(strSeconds[0]) * 3600 + strSeconds[1] * 60 + Number(strSeconds[2])): seconds; } return 0; }, diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js index 4453a7ceca38bf1dfa7d24a9ef25df4d499f1e1d..a11cde4f0e02ca0c3e3e16cf046abcb0d5a605d2 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js @@ -1,4 +1,5 @@ import UnitConverter from "./unit.converter"; +import _ from 'lodash'; const Validator = { validateTime(value) { @@ -62,8 +63,7 @@ const Validator = { } else { var timeFormat = /^([0-9]|[0-1][0-9]|[0-2][0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$/; isValid = timeFormat.test(time); - } - + } } else { isValid = false; } @@ -96,7 +96,34 @@ const Validator = { } else { return false; } - } + }, + validateTransitOffset(schema, jsonOutput, error, path) { + const tmpTime = _.split(jsonOutput, ":"); + if (jsonOutput.length === 0 && schema.required === true) { + error.push({ + message:`Transit Offset - ${schema.title} is required. Time format should be [+/- Hours:Minutes:Seconds]. eg. '-23:59:59', '+20:23:25', '15:45:45'`, + path:path, + property:'validationType', + }); + } + //here the isValidHHmmss() valiadtion function not used because it requires only MM:SS validation and the hours may define more than 23 + else if (tmpTime.length !== 3 || tmpTime[1]>59 || tmpTime[1].trim() === '' || tmpTime[2]>59 || tmpTime[2].trim() === '') { + error.push({ + message:"Invalid time format. Time format should be [+/- Hours:Minutes:Seconds]. eg. '-23:59:59', '+20:23:25', '15:45:45'", + path:path, + property:'validationType', + }); + } else { + let value = UnitConverter.getHHmmssToSecs(jsonOutput); + if (isNaN(value) || (value < schema.minimum || value > schema.maximum)) { + error.push({ + message:'Time must be between '+((schema.minimum<0)?'-':'')+UnitConverter.getSecsToHHmmss(schema.minimum)+' and '+((schema.maximum<0)?'-':'')+UnitConverter.getSecsToHHmmss(schema.maximum), + path:path, + property:'validationType', + }); + } + } + }, }; export default Validator;