diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py index 2b68de96458c95d1cd2e068f7916dc9851ff3a45..550efab2b7be2627304ce24c24f4cf95cd5cb9c0 100644 --- a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py +++ b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py @@ -283,7 +283,9 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod target_rise_and_set_times = coordinates_timestamps_and_stations_to_target_rise_and_set(angle1=angle1, angle2=angle2, direction_type=direction_type, timestamps=timestamps, stations=tuple(stations), angle_to_horizon=min_elevation) for station, times in target_rise_and_set_times.items(): for i in range(len(timestamps)): - if not (timestamps[i] > times[0]['rise'] and timestamps[i] < times[0]['set']): + if times[0]['always_above_horizon']: + continue + if times[0]['always_below_horizon'] or not (timestamps[i] > times[0]['rise'] and timestamps[i] < times[0]['set']): if task['specifications_template'] == 'calibrator observation': logger.info('min_calibrator_elevation=%s constraint is not met at timestamp=%s' % (min_elevation.rad, timestamps[i])) else: diff --git a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py index 98c1c41ea5d494bcff75f928fc2da1216777ff51..e8fadb2c6085117007f7913c8ecee0fa3808b434 100755 --- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py @@ -735,8 +735,11 @@ class TestSkyConstraints(unittest.TestCase): self.distance_mock.return_value = self.distance_data self.addCleanup(self.distance_patcher.stop) - self.target_rise_and_set_data = {"CS002": [{"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0)}, - {"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0)}]} + self.target_rise_and_set_data = {"CS002": [{"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0), "always_above_horizon": False, "always_below_horizon": False}, + {"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0), "always_above_horizon": False, "always_below_horizon": False}]} + self.target_rise_and_set_data_always_above = {"CS002": [{"rise": None, "set": None, "always_above_horizon": True, "always_below_horizon": False}]} + self.target_rise_and_set_data_always_below = {"CS002": [{"rise": None, "set": None, "always_above_horizon": False, "always_below_horizon": True}]} + self.target_rise_and_set_patcher = mock.patch('lofar.sas.tmss.services.scheduling.constraints.template_constraints_v1.coordinates_timestamps_and_stations_to_target_rise_and_set') self.target_rise_and_set_mock = self.target_rise_and_set_patcher.start() self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data @@ -760,21 +763,45 @@ class TestSkyConstraints(unittest.TestCase): # min_target_elevation - def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_true_when_met(self): + def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_true(self): + self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data + self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1} self.scheduling_unit_blueprint.save() - timestamp = datetime(2020, 1, 1, 10, 0, 0) + timestamp = datetime(2020, 1, 1, 10, 0, 0) # target sets after obs ends (mocked response) returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) self.assertTrue(returned_value) - def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_false_when_not_met(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.2} + def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_when_target_always_above_returns_true(self): + self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data_always_above + + self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1} self.scheduling_unit_blueprint.save() - timestamp = datetime(2020, 1, 1, 11, 0, 0) + timestamp = datetime(2020, 1, 1, 10, 0, 0) # target is always up (mocked response) + returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) + self.assertTrue(returned_value) + + def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_false(self): + self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data + + self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1} + self.scheduling_unit_blueprint.save() + timestamp = datetime(2020, 1, 1, 11, 0, 0) # target sets before obs ends (mocked response) + returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) + self.assertFalse(returned_value) + + def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_when_target_is_always_below_returns_false(self): + self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data_always_below + + self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1} + self.scheduling_unit_blueprint.save() + timestamp = datetime(2020, 1, 1, 10, 0, 0) # target is never up (mocked response) returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) self.assertFalse(returned_value) + + class TestTimeConstraints(TestCase): """ Tests for the time constraint checkers used in dynamic scheduling with different boundaries diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py b/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py index ae926e172f4a39a4ff77a442346fbf25d4505e35..3c0e184ce79ac8e697043dcf8ced5dceba3bf1eb 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py @@ -126,9 +126,10 @@ def coordinates_timestamps_and_stations_to_target_rise_and_set(angle1: float, an :param stations: tuple of station names, e.g. ("CS002",) :param angle_to_horizon: the angle between horizon and given coordinates for which rise and set times are returned :return A dict that maps station names to a list of dicts with rise and set times for each requested date. + If rise and set are None, the target is always above or below horizon, and the respective boolean is True. E.g. - {"CS002": [{"rise": datetime(2020, 1, 1, 4, 0, 0), "set": datetime(2020, 1, 1, 11, 0, 0)}, - {"rise": datetime(2020, 1, 2, 4, 0, 0), "set": datetime(2020, 1, 2, 11, 0, 0)}] + {"CS002": [{"rise": datetime(2020, 1, 1, 4, 0, 0), "set": datetime(2020, 1, 1, 11, 0, 0), "always_above_horizon": False, "always_below_horizon": False}, + {"rise": datetime(2020, 1, 2, 4, 0, 0), "set": datetime(2020, 1, 2, 11, 0, 0), "always_above_horizon": False, "always_below_horizon": False}] } """ if direction_type == "J2000": @@ -140,10 +141,29 @@ def coordinates_timestamps_and_stations_to_target_rise_and_set(angle1: float, an for timestamp in timestamps: # todo: this can probably be made faster by moving the following logic to an own function with single station/timestamp as input and putting the lru_cache on there. observer = create_astroplan_observer_for_station(station) - target_set = observer.target_set_time(target=coord, time=Time(timestamp), horizon=angle_to_horizon, which='next', n_grid_points=TARGET_SET_RISE_PRECISION) - target_rise = observer.target_rise_time(target=coord, time=Time(target_set), horizon=angle_to_horizon, which='previous', n_grid_points=TARGET_SET_RISE_PRECISION) + try: + target_set = observer.target_set_time(target=coord, time=Time(timestamp), horizon=angle_to_horizon, which='next', n_grid_points=TARGET_SET_RISE_PRECISION) + target_rise = observer.target_rise_time(target=coord, time=Time(target_set), horizon=angle_to_horizon, which='previous', n_grid_points=TARGET_SET_RISE_PRECISION) + return_dict.setdefault(station, []).append( + {"rise": target_rise.to_datetime(), + "set": target_set.to_datetime(), + "always_above_horizon": False, + "always_below_horizon": False}) + except TypeError as e: + if "numpy.float64" in str(e): + # Note: when the target is always above or below horizon, astroplan excepts with the not very + # meaningful error: 'numpy.float64' object does not support item assignment + # Determine whether the target is always above or below horizon so that we can return some useful + # additional info, e.g. for scheduling purposes. + is_up = observer.target_is_up(target=coord, time=Time(timestamp), horizon=angle_to_horizon) + return_dict.setdefault(station, []).append( + {"rise": None, + "set": None, + "always_above_horizon": is_up, + "always_below_horizon": not is_up}) + else: + raise - return_dict.setdefault(station, []).append({"rise": target_rise.to_datetime(), "set": target_set.to_datetime()}) return return_dict diff --git a/SAS/TMSS/backend/test/t_conversions.py b/SAS/TMSS/backend/test/t_conversions.py index 7f8d66d6e4b8758b3cf13bf04bf3d8488deb89ad..1773168c7b1ded14c41aee27f0fddd6683d9f9f7 100755 --- a/SAS/TMSS/backend/test/t_conversions.py +++ b/SAS/TMSS/backend/test/t_conversions.py @@ -362,6 +362,35 @@ class UtilREST(unittest.TestCase): self.assertNotEqual(rise, rise_last) rise_last = rise + def test_util_target_rise_and_set_detects_when_target_above_horizon(self): + + # assert always below and always above are usually false + r = requests.get(BASE_URL + '/util/target_rise_and_set?angle1=0.5&angle2=0.8×tamps=2020-01-01&horizon=0.2', auth=AUTH) + self.assertEqual(r.status_code, 200) + r_dict = json.loads(r.content.decode('utf-8')) + self.assertIsNotNone(r_dict['CS002'][0]['rise']) + self.assertIsNotNone(r_dict['CS002'][0]['set']) + self.assertFalse(r_dict['CS002'][0]['always_below_horizon']) + self.assertFalse(r_dict['CS002'][0]['always_above_horizon']) + + # assert rise and set are None and flag is true when target is always above horizon + r = requests.get(BASE_URL + '/util/target_rise_and_set?angle1=0.5&angle2=0.8×tamps=2020-01-01&horizon=0.1', auth=AUTH) + self.assertEqual(r.status_code, 200) + r_dict = json.loads(r.content.decode('utf-8')) + self.assertIsNone(r_dict['CS002'][0]['rise']) + self.assertIsNone(r_dict['CS002'][0]['set']) + self.assertTrue(r_dict['CS002'][0]['always_above_horizon']) + self.assertFalse(r_dict['CS002'][0]['always_below_horizon']) + + # assert rise and set are None and flag is true when target is always below horizon + r = requests.get(BASE_URL + '/util/target_rise_and_set?angle1=0.5&angle2=-0.5×tamps=2020-01-01&horizon=0.2', auth=AUTH) + self.assertEqual(r.status_code, 200) + r_dict = json.loads(r.content.decode('utf-8')) + self.assertIsNone(r_dict['CS002'][0]['rise']) + self.assertIsNone(r_dict['CS002'][0]['set']) + self.assertFalse(r_dict['CS002'][0]['always_above_horizon']) + self.assertTrue(r_dict['CS002'][0]['always_below_horizon']) + if __name__ == "__main__": os.environ['TZ'] = 'UTC' diff --git a/SAS/TMSS/frontend/tmss_webapp/package.json b/SAS/TMSS/frontend/tmss_webapp/package.json index e9cc1d244a28ffcb034292897693fb7875c7a0f9..5de0cf8841f3e116bcd8cf264c26613650b2467f 100644 --- a/SAS/TMSS/frontend/tmss_webapp/package.json +++ b/SAS/TMSS/frontend/tmss_webapp/package.json @@ -38,6 +38,7 @@ "react-bootstrap-datetimepicker": "0.0.22", "react-calendar-timeline": "^0.27.0", "react-dom": "^16.13.1", + "react-flatpickr": "^3.10.7", "react-frame-component": "^4.1.2", "react-json-to-table": "^0.1.7", "react-json-view": "^1.19.1", 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 98d84429009f475b44411113fe6d7d6d319dcf88..50623578335782048c11ba4ff25bb4f182370119 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/CustomDateComp.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/CustomDateComp.js @@ -1,7 +1,11 @@ import React, { Component } from 'react'; +import Flatpickr from "react-flatpickr"; import {Calendar} from 'primereact/calendar'; import moment from 'moment'; import UIConstants from '../../utils/ui.constants'; +import UtilService from '../../services/util.service'; + +import "flatpickr/dist/flatpickr.css"; //const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; @@ -16,9 +20,13 @@ export default class CustomDateComp extends Component { componentDidMount(){ let parentRows = this.props.agGridReact.props.rowData[this.props.node.rowIndex]; let parentCellData = parentRows[this.props.colDef.field]; - this.setState({ - date:parentCellData - }) + UtilService.getUTC() + .then(systemTime => { + this.setState({ + date:parentCellData, + systemTime: moment.utc(systemTime) + }) + }); } isPopup() { @@ -33,30 +41,30 @@ export default class CustomDateComp extends Component { } render() { - return ( - <Calendar - d dateFormat = {UIConstants.CALENDAR_DATE_FORMAT} - value= {this.state.date} - onChange= {e => {this.updateDateChanges(e)}} - // onBlur= {e => {this.updateDateChanges(e)}} - //data-testid="start" - todayButtonClassName="today-calendar-btn" - showButtonBar - showTime= {true} - showSeconds= {true} - hourFormat= "24" - showIcon= {false} inline - /> - ); + return this.state.systemTime?( + <Flatpickr + data-enable-time + options={{ + "inline": true, + "enableSeconds": true, + "time_24hr": true, + "defaultDate": this.state.systemTime?this.state.systemTime.format(UIConstants.CALENDAR_DEFAULTDATE_FORMAT):"", + "defaultHour": this.state.systemTime?this.state.systemTime.hours():12, + "defaultMinute": this.state.systemTime?this.state.systemTime.minutes():0 + }} + value={this.state.date} + onChange= {value => {this.updateDateChanges(value[0]?value[0]:this.state.date)}} + /> + ):""; } updateDateChanges(e){ - this.setState({date : e.value || ''}); + this.setState({date : e || ''}); } ondatechange(e){ - this.setState({date : e.value}); + this.setState({date : e}); } getDate() { 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 35725047eaeceb182457875029cbba90b4cd7320..396b74fd9c413e4ca93798b76ba3462889ac7dd0 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 @@ -4,6 +4,7 @@ import _ from 'lodash'; import Jeditor from '../../components/JSONEditor/JEditor'; import UnitConversion from '../../utils/unit.converter'; import UIConstants from '../../utils/ui.constants'; +import UtilService from '../../services/util.service'; /* eslint-disable react-hooks/exhaustive-deps */ export default (props) => { @@ -11,6 +12,7 @@ export default (props) => { const { parentFunction = (editorFn) => { editorFunction = editorFn;} } = props; const [constraintSchema, setConstraintSchema] = useState(); const [initialValue, setInitialValue] = useState(); + const [systemTime, setSystemTime] = useState(); //SU Constraint Editor Property Order,format and validation const configureProperties = (properties) => { for (const propertyKey in properties) { @@ -69,7 +71,8 @@ export default (props) => { } }; //DateTime flatPicker component enabled with seconds - const setDateTimeOption = (propertyValue) => { + const setDateTimeOption = async(propertyValue) => { + const systemTime = moment.utc((await UtilService.getUTC())); propertyValue.format = 'datetime-local'; propertyValue.validationType = 'dateTime'; propertyValue.skipFormat = true; @@ -83,6 +86,9 @@ export default (props) => { "enableSeconds": true, "time_24hr": true, "allowInput": true, + "defaultDate": systemTime.format(UIConstants.CALENDAR_DEFAULTDATE_FORMAT), + "defaultHour": systemTime.hour(), + "defaultMinute": systemTime.minutes() } }; }; @@ -101,7 +107,7 @@ export default (props) => { } else if(definitionName === 'timewindow') { for (let property in schema.definitions.timewindow.properties) { if(property === 'to' || property === 'from'){ - setDateTimeOption(schema.definitions.timewindow.properties[property]); + // setDateTimeOption(schema.definitions.timewindow.properties[property]); if (property === 'from') { schema.definitions.timewindow.properties[property].propertyOrder = 1; } else { @@ -148,7 +154,9 @@ export default (props) => { } } - const constraintStrategy = () => { + const constraintStrategy = async() => { + const currentSystemTime = moment.utc(await UtilService.getUTC()) + setSystemTime(currentSystemTime); // const constraintTemplate = { ...props.constraintTemplate } const constraintTemplate = _.cloneDeep(props.constraintTemplate); if (constraintTemplate.schema) { @@ -196,6 +204,9 @@ export default (props) => { if (!props.constraintTemplate) { return; } + UtilService.getUTC().then(utcTime => { + setSystemTime(moment.utc(utcTime)); + }); if (props.initValue) { modifyInitiValue(); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/reservation.create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/reservation.create.js index 9436b57558dd41712e24a988ccb18751884cb32a..e1b884053169d5a05dd9ab001e45af3c7ae0804a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/reservation.create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/reservation.create.js @@ -6,6 +6,7 @@ import { Growl } from 'primereact/components/growl/Growl'; import AppLoader from '../../layout/components/AppLoader'; import PageHeader from '../../layout/components/PageHeader'; import UIConstants from '../../utils/ui.constants'; +import Flatpickr from "react-flatpickr"; import { Calendar } from 'primereact/calendar'; import { InputMask } from 'primereact/inputmask'; import { Dropdown } from 'primereact/dropdown'; @@ -18,6 +19,9 @@ import ProjectService from '../../services/project.service'; import ReservationService from '../../services/reservation.service'; import UnitService from '../../utils/unit.converter'; import Jeditor from '../../components/JSONEditor/JEditor'; +import UtilService from '../../services/util.service'; + +import "flatpickr/dist/flatpickr.css"; /** * Component to create a new Reservation @@ -38,7 +42,7 @@ export class ReservationCreate extends Component { reservation: { name: '', description: '', - start_time: '', + start_time: null, duration: '', project: (props.match?props.match.params.project:null) || null, }, @@ -78,9 +82,11 @@ export class ReservationCreate extends Component { async initReservation() { const promises = [ ProjectService.getProjectList(), ReservationService.getReservationTemplates(), + UtilService.getUTC() ]; let emptyProjects = [{url: null, name: "Select Project"}]; Promise.all(promises).then(responses => { + let systemTime = moment.utc(responses[2]); this.projects = emptyProjects.concat(responses[0]); this.reservationTemplates = responses[1]; @@ -95,8 +101,9 @@ export class ReservationCreate extends Component { paramsSchema: schema, isLoading: false, reservationTemplate: reservationTemplate, + systemTime: systemTime }); - }); + }); } @@ -209,7 +216,6 @@ export class ReservationCreate extends Component { } } } - this.setState({errors: errors, validFields: validFields}); if (Object.keys(validFields).length === Object.keys(this.formRules).length) { validForm = true; @@ -356,20 +362,27 @@ export class ReservationCreate extends Component { <div className="p-field p-grid"> <label htmlFor="reservationName" className="col-lg-2 col-md-2 col-sm-12">From Date <span style={{color:'red'}}>*</span></label> <div className="col-lg-3 col-md-3 col-sm-12"> - <Calendar - d dateFormat="yy-mm-dd" - value= {this.state.reservation.start_time} - onChange= {e => this.setParams('start_time',e.value)} - data-testid="start_time" - tooltip="Moment at which the reservation starts from, that is, when its reservation can run." tooltipOptions={this.tooltipOptions} - showIcon={true} - showTime= {true} - showSeconds= {true} - hourFormat= "24" - /> - - <label className={this.state.errors.from?"error":"info"}> - {this.state.errors.start_time ? this.state.errors.start_time : ""} + <Flatpickr data-enable-time data-input options={{ + "inlineHideInput": true, + "wrap": true, + "enableSeconds": true, + "time_24hr": true, + "allowInput": true, + "defaultDate": this.state.systemTime.format(UIConstants.CALENDAR_DEFAULTDATE_FORMAT), + "defaultHour": this.state.systemTime.hours(), + "defaultMinute": this.state.systemTime.minutes() + }} + title="Start of this reservation" + value={this.state.reservation.start_time} + onChange= {value => {this.setParams('start_time', value[0]?value[0]:this.state.reservation.start_time); + this.setReservationParams('start_time', value[0]?value[0]:this.state.reservation.start_time)}} > + <input type="text" data-input className={`p-inputtext p-component ${this.state.errors.start_time && this.state.touched.start_time?'input-error':''}`} /> + <i className="fa fa-calendar" data-toggle style={{position: "absolute", marginLeft: '-25px', marginTop:'5px', cursor: 'pointer'}} ></i> + <i className="fa fa-times" style={{position: "absolute", marginLeft: '-50px', marginTop:'5px', cursor: 'pointer'}} + onClick={e => {this.setParams('start_time', ''); this.setReservationParams('start_time', '')}}></i> + </Flatpickr> + <label className={this.state.errors.start_time && this.state.touched.start_time?"error":"info"}> + {this.state.errors.start_time && this.state.touched.start_time ? this.state.errors.start_time : ""} </label> </div> <div className="col-lg-1 col-md-1 col-sm-12"></div> @@ -380,6 +393,8 @@ export class ReservationCreate extends Component { value={this.state.reservation.duration} mask="99:99:99" placeholder="HH:mm:ss" + tooltip="Duration of this reservation. If it is empty, then this reservation is indefinite." + tooltipOptions={this.tooltipOptions} onChange= {e => this.setParams('duration',e.value)} ref={input =>{this.input = input}} /> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js index 828386d19450af0473e199ad44430a7b4491e8ed..56437953e4ee0ed1bd91c69fda2fd9512bb7f703 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js @@ -845,9 +845,9 @@ export class TimelineView extends Component { if (this.state.redirect) { return <Redirect to={ {pathname: this.state.redirect} }></Redirect> } - if (this.state.loader) { - return <AppLoader /> - } + // if (this.state.loader) { + // return <AppLoader /> + // } const isSUDetsVisible = this.state.isSUDetsVisible; const isTaskDetsVisible = this.state.isTaskDetsVisible; const canExtendSUList = this.state.canExtendSUList;